mirror of
https://github.com/mamoe/mirai.git
synced 2025-05-06 05:45:19 +08:00
[core] Update docs for implementations
This commit is contained in:
parent
c383d3bf9c
commit
eeb10cc89a
mirai-core-utils/src/commonMain/kotlin
mirai-core/src/commonMain/kotlin/network
@ -18,8 +18,15 @@ import kotlin.reflect.KProperty
|
||||
private val UNINITIALIZED: Any? = Symbol("UNINITIALIZED")
|
||||
|
||||
/**
|
||||
* - [initializer] is supported to be called at most once, however multiple invocations may happen if executed by multiple coroutines in single thread.
|
||||
* - [ReadWriteProperty.setValue] prevails on competition with [initializer].
|
||||
* Creates a lazily initialized, atomic, mutable property.
|
||||
*
|
||||
* [initializer] will be called at most once for most of the time, but not always.
|
||||
* Multiple invocations may happen if property getter is called by multiple coroutines in single thread (implementation use reentrant lock).
|
||||
* Hence, you must not trust [initializer] to be called only once.
|
||||
*
|
||||
* If property setter is executed before any execution of getter, [initializer] will not be called.
|
||||
* While [initializer] is running, i.e. still calculating the value to set to the property,
|
||||
* calling property setter will *outdo* the initializer. That is, the setter always prevails on competition with [initializer].
|
||||
*/
|
||||
public fun <T> lateinitMutableProperty(initializer: () -> T): ReadWriteProperty<Any?, T> =
|
||||
LateinitMutableProperty(initializer)
|
||||
@ -38,7 +45,7 @@ private class LateinitMutableProperty<T>(
|
||||
val initializer = initializer
|
||||
if (initializer != null && this.value.value === UNINITIALIZED) {
|
||||
val value = initializer()
|
||||
this.initializer = null
|
||||
this.initializer = null // not used anymore, help gc
|
||||
this.value.compareAndSet(UNINITIALIZED, value) // setValue prevails
|
||||
this.value.value.let {
|
||||
check(it !== UNINITIALIZED)
|
||||
@ -46,6 +53,7 @@ private class LateinitMutableProperty<T>(
|
||||
}
|
||||
} else this.value.value as T
|
||||
}
|
||||
|
||||
else -> v as T
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ package net.mamoe.mirai.utils
|
||||
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* An object with name for debugging purposes. Handy for states.
|
||||
*/
|
||||
public class Symbol private constructor(name: String) {
|
||||
private val str = "Symbol($name)"
|
||||
override fun toString(): String = str
|
||||
|
@ -88,7 +88,7 @@ internal class PacketCodecException(
|
||||
OTHER,
|
||||
}
|
||||
|
||||
// not available in native
|
||||
// not available in native
|
||||
// override fun getStackTrace(): Array<StackTraceElement> {
|
||||
// return targetException.stackTrace
|
||||
// }
|
||||
|
@ -63,6 +63,7 @@ internal abstract class CommonNetworkHandler<Conn>(
|
||||
setState { StateClosed(error) }
|
||||
return
|
||||
}
|
||||
|
||||
PacketCodecException.Kind.PROTOCOL_UPDATED -> passToExceptionHandler()
|
||||
PacketCodecException.Kind.OTHER -> passToExceptionHandler()
|
||||
}
|
||||
@ -76,7 +77,7 @@ internal abstract class CommonNetworkHandler<Conn>(
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Creates a connection
|
||||
* Creates a connection.
|
||||
*/
|
||||
protected abstract suspend fun createConnection(): Conn
|
||||
|
||||
@ -85,9 +86,17 @@ internal abstract class CommonNetworkHandler<Conn>(
|
||||
*/
|
||||
protected abstract fun Conn.writeAndFlushOrCloseAsync(packet: OutgoingPacket)
|
||||
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
/**
|
||||
* Close this connection for general reason, immediately. Immediately means any live objects with lifecycle must be marked *cancelled*, *dead* or any terminate state.
|
||||
*/
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "KotlinRedundantDiagnosticSuppress") // can happen on some platforms
|
||||
protected abstract fun Conn.close()
|
||||
|
||||
/**
|
||||
* *Single-thread* event-loop packet decoder.
|
||||
*
|
||||
* Call [PacketDecodePipeline.send] to submit a decoding job.
|
||||
*/
|
||||
internal inner class PacketDecodePipeline(parentContext: CoroutineContext) :
|
||||
CoroutineScope by parentContext.childScope() {
|
||||
private val packetCodec: PacketCodec by lazy { context[PacketCodec] }
|
||||
|
@ -25,6 +25,7 @@ import net.mamoe.mirai.internal.network.handler.selector.SelectorNetworkHandler
|
||||
import net.mamoe.mirai.internal.network.handler.state.StateObserver
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.PacketFactory
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
|
||||
/**
|
||||
@ -127,12 +128,6 @@ internal interface NetworkHandler : CoroutineScope {
|
||||
suspend fun resumeConnection()
|
||||
|
||||
|
||||
suspend fun <P : Packet?> sendAndExpect(
|
||||
packet: OutgoingPacketWithRespType<P>,
|
||||
timeout: Long = 5000,
|
||||
attempts: Int = 2
|
||||
): P
|
||||
|
||||
/**
|
||||
* Sends [packet], suspends and expects to receive a response from the server.
|
||||
*
|
||||
@ -145,6 +140,19 @@ internal interface NetworkHandler : CoroutineScope {
|
||||
*
|
||||
* @param attempts ranges `1..INFINITY`
|
||||
*/
|
||||
suspend fun <P : Packet?> sendAndExpect(
|
||||
packet: OutgoingPacketWithRespType<P>,
|
||||
timeout: Long = 5000,
|
||||
attempts: Int = 2
|
||||
): P
|
||||
|
||||
/**
|
||||
* Sends [packet], suspends and expects to receive a response from the server.
|
||||
*
|
||||
* Note that it's corresponding [PacketFactory]'s responsibility to decode the returned date from server to give resultant [Packet],
|
||||
* and that packet is cast to [P], hence unsafe (vulnerable for [ClassCastException]).
|
||||
* Always use [sendAndExpect] with [OutgoingPacketWithRespType], use this unsafe overload only for legacy code.
|
||||
*/
|
||||
suspend fun <P : Packet?> sendAndExpect(packet: OutgoingPacket, timeout: Long = 5000, attempts: Int = 2): P
|
||||
|
||||
/**
|
||||
@ -173,13 +181,16 @@ internal interface NetworkHandler : CoroutineScope {
|
||||
internal val NetworkHandler.logger: MiraiLogger get() = context.logger
|
||||
|
||||
/**
|
||||
* Suspend coroutine to wait for the state [suspendUntil].
|
||||
* Suspends coroutine to wait for the state [suspendUntil].
|
||||
*/
|
||||
internal suspend fun NetworkHandler.awaitState(suspendUntil: NetworkHandler.State) {
|
||||
if (this.state == suspendUntil) return
|
||||
stateChannel.consumeAsFlow().takeWhile { it != suspendUntil }.collect()
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspends coroutine to wait for a state change. Returns immediately after [NetworkHandler.state] has changed.
|
||||
*/
|
||||
internal suspend fun NetworkHandler.awaitStateChange() {
|
||||
stateChannel.consumeAsFlow().first()
|
||||
}
|
@ -35,7 +35,7 @@ import kotlin.jvm.Volatile
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Implements basic logics of [NetworkHandler]
|
||||
* Implements a state-based [NetworkHandler].
|
||||
*/
|
||||
internal abstract class NetworkHandlerSupport(
|
||||
final override val context: NetworkHandlerContext,
|
||||
@ -45,14 +45,24 @@ internal abstract class NetworkHandlerSupport(
|
||||
additionalCoroutineContext.childScopeContext(SupervisorJob(context.bot.coroutineContext.job))
|
||||
.plus(CoroutineExceptionHandler.fromMiraiLogger(logger))
|
||||
|
||||
/**
|
||||
* Creates an instance of the initial state. This is guaranteed to be called at most once.
|
||||
*/
|
||||
protected abstract fun initialState(): BaseStateImpl
|
||||
|
||||
/**
|
||||
* It's not guaranteed whether this function sends the packet in-place or launches a coroutine for it.
|
||||
* Caller should not rely on this property.
|
||||
* Performs network IO or launches a coroutine for that, to send the [packet].
|
||||
*
|
||||
* **Node**: It's not guaranteed whether this function sends the packet in-place or launches a coroutine for it.
|
||||
* Caller should not rely on this characteristic.
|
||||
*/
|
||||
protected abstract suspend fun sendPacketImpl(packet: OutgoingPacket)
|
||||
|
||||
/**
|
||||
* Handles *unknown* [RawIncomingPacket]s. Packets with unrecognized [RawIncomingPacket.commandName] are considered *unknown*.
|
||||
*
|
||||
* Can be called by implementation
|
||||
*/
|
||||
protected fun collectUnknownPacket(raw: RawIncomingPacket) {
|
||||
packetLogger.debug { "Unknown packet: commandName=${raw.commandName}, body=${raw.body.toUHexString()}" }
|
||||
// may add hooks here (to context)
|
||||
@ -137,6 +147,9 @@ internal abstract class NetworkHandlerSupport(
|
||||
sendPacketImpl(packet)
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for the resultant packet from server.
|
||||
*/
|
||||
protected class PacketListener(
|
||||
val commandName: String,
|
||||
val sequenceId: Int,
|
||||
@ -231,7 +244,7 @@ internal abstract class NetworkHandlerSupport(
|
||||
/**
|
||||
* State is *lazy*, initialized only if requested.
|
||||
*
|
||||
* You need to call setter inside `synchronized(this) { }`.
|
||||
* You must not set this property directly, but use [setState].
|
||||
*/
|
||||
@Suppress("PropertyName")
|
||||
protected var _state: BaseStateImpl by lateinitMutableProperty { initialState() }
|
||||
|
Loading…
Reference in New Issue
Block a user