Improve setState stability

This commit is contained in:
Him188 2022-05-26 00:30:25 +01:00
parent ff2a8acb0c
commit 92942c1037

View File

@ -10,7 +10,8 @@
package net.mamoe.mirai.internal.network.handler package net.mamoe.mirai.internal.network.handler
import kotlinx.atomicfu.locks.SynchronizedObject import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized import kotlinx.atomicfu.locks.reentrantLock
import kotlinx.atomicfu.locks.withLock
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.channels.ReceiveChannel
@ -24,7 +25,6 @@ import net.mamoe.mirai.internal.network.handler.state.StateObserver
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket 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.OutgoingPacketWithRespType
import net.mamoe.mirai.internal.utils.SingleEntrantLock
import net.mamoe.mirai.internal.utils.fromMiraiLogger import net.mamoe.mirai.internal.utils.fromMiraiLogger
import net.mamoe.mirai.internal.utils.subLogger import net.mamoe.mirai.internal.utils.subLogger
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
@ -247,8 +247,7 @@ internal abstract class NetworkHandlerSupport(
* You may need to call [BaseStateImpl.resumeConnection] to activate the new state, as states are lazy. * You may need to call [BaseStateImpl.resumeConnection] to activate the new state, as states are lazy.
*/ */
protected inline fun <reified S : BaseStateImpl> setState(noinline new: () -> S): S? = protected inline fun <reified S : BaseStateImpl> setState(noinline new: () -> S): S? =
@OptIn(TestOnly::class) _state.setState(new)
setStateImpl(S::class as KClass<S>?, new)
/** /**
* Attempts to change state if current state is [this]. * Attempts to change state if current state is [this].
@ -259,16 +258,17 @@ internal abstract class NetworkHandlerSupport(
*/ */
protected inline fun <reified S : BaseStateImpl> BaseStateImpl.setState( protected inline fun <reified S : BaseStateImpl> BaseStateImpl.setState(
noinline new: () -> S, noinline new: () -> S,
): S? = synchronized(lockForSetStateWithOldInstance) { ): S? = lock.withLock {
if (_state === this) { if (_state === this) {
this@NetworkHandlerSupport.setState(new) @OptIn(TestOnly::class)
this@NetworkHandlerSupport.setStateImpl(S::class, new)
} else { } else {
null null
} }
} }
private val lock = SingleEntrantLock() private val lock = reentrantLock()
private val lockForSetStateWithOldInstance = SynchronizedObject() internal val lockForSetStateWithOldInstance = SynchronizedObject()
/** /**
* This can only be called by [setState] or in tests. * This can only be called by [setState] or in tests.
@ -278,7 +278,7 @@ internal abstract class NetworkHandlerSupport(
// //
@TestOnly @TestOnly
internal fun <S : BaseStateImpl> setStateImpl(newType: KClass<S>?, new: () -> S): S? = internal fun <S : BaseStateImpl> setStateImpl(newType: KClass<S>?, new: () -> S): S? =
lock.withLock(newType ?: lock) { lock.withLock {
val old = _state val old = _state
if (newType != null && old::class == newType) return@withLock null // already set to expected state by another thread. Avoid replications. if (newType != null && old::class == newType) return@withLock null // already set to expected state by another thread. Avoid replications.
if (old.correspondingState == NetworkHandler.State.CLOSED) return@withLock null // CLOSED is final. if (old.correspondingState == NetworkHandler.State.CLOSED) return@withLock null // CLOSED is final.