diff --git a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt index da1d3b949..3cebf42c8 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.handler @@ -159,6 +159,26 @@ internal abstract class NetworkHandlerSupport( final override val coroutineContext: CoroutineContext = this@NetworkHandlerSupport.coroutineContext + Job(this@NetworkHandlerSupport.coroutineContext.job) + // Do not use init blocks to launch anything. Do use [startState] + + /** + * Starts things that should be done in this state. + * + * Called after this instance is initialized, and it is at suitable time for initialization. + * + * Note: must be fast. + */ + open fun startState() { + + } + + /** + * Called after this instance is set to [_state]. (Visible publicly) + */ + open fun afterUpdated() { + + } + open fun getCause(): Throwable? = null /** @@ -248,7 +268,7 @@ internal abstract class NetworkHandlerSupport( val stateObserver = context.getOrNull(StateObserver) val impl = try { - new() // inline only once + new() } catch (e: Throwable) { stateObserver?.exceptionOnCreatingNewState(this, old, e) throw e @@ -257,8 +277,14 @@ internal abstract class NetworkHandlerSupport( check(old !== impl) { "Old and new states cannot be the same." } stateObserver?.beforeStateChanged(this, old, impl) + + // We should startState before expose it publicly because State.resumeConnection may wait for some jobs that are launched in startState. + // We cannot close old state before changing the 'public' _state to be the new one, otherwise every client will get some kind of exceptions (unspecified, maybe CancellationException). + impl.startState() // launch jobs _state = impl // update current state old.cancel(StateSwitchingException(old, impl)) // close old + impl.afterUpdated() // now do post-update things. + stateObserver?.stateChanged(this, old, impl) // notify observer _stateChannel.trySend(impl.correspondingState) // notify selector diff --git a/mirai-core/src/commonMain/kotlin/network/handler/state/StateObserver.kt b/mirai-core/src/commonMain/kotlin/network/handler/state/StateObserver.kt index 10aa3b708..2e0180501 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/state/StateObserver.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/state/StateObserver.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.handler.state @@ -22,7 +22,7 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport internal interface StateObserver { /** - * Called when _state is being changed_, where [NetworkHandlerSupport._state] is still [previous]. + * Called when _state is being changed_, where [NetworkHandlerSupport._state] is still [previous], and new state is not yet started. */ fun beforeStateChanged( networkHandler: NetworkHandlerSupport, diff --git a/mirai-core/src/commonMain/kotlin/network/impl/netty/NettyNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/impl/netty/NettyNetworkHandler.kt index 1c4887f6e..bca7c378d 100644 --- a/mirai-core/src/commonMain/kotlin/network/impl/netty/NettyNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/impl/netty/NettyNetworkHandler.kt @@ -259,14 +259,16 @@ internal open class NettyNetworkHandler( */ private val collectiveExceptions: ExceptionCollector, ) : NettyState(State.CONNECTING) { - private val connection = async { - createConnection() - } + private lateinit var connection: Deferred @Suppress("JoinDeclarationAndAssignment") - private val connectResult: Deferred + private lateinit var connectResult: Deferred + + override fun startState() { + connection = async { + createConnection() + } - init { connectResult = async { connection.join() context[SsoProcessor].login(this@NettyNetworkHandler) @@ -318,7 +320,8 @@ internal open class NettyNetworkHandler( protected inner class StateLoading( private val connection: NettyChannel, ) : NettyState(State.LOADING) { - init { + + override fun startState() { coroutineContext.job.invokeOnCompletion { if (it != null) { connection.close() @@ -351,7 +354,7 @@ internal open class NettyNetworkHandler( private val connection: NettyChannel, private val configPush: Job, ) : NettyState(State.OK) { - init { + override fun startState() { coroutineContext.job.invokeOnCompletion { err -> if (err is StateSwitchingException) { if (err.new.correspondingState == State.CLOSED) { @@ -396,7 +399,8 @@ internal open class NettyNetworkHandler( protected inner class StateClosed( val exception: Throwable?, ) : NettyState(State.CLOSED) { - init { + + override fun afterUpdated() { close(exception) }