mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-05 07:10:11 +08:00
core, network: Guard changing state
This commit is contained in:
parent
1b0a8cb2d5
commit
503704a61b
@ -280,11 +280,8 @@ internal abstract class NetworkHandlerSupport(
|
||||
private val lock = reentrantLock()
|
||||
internal val lockForSetStateWithOldInstance = SynchronizedObject()
|
||||
|
||||
/**
|
||||
* Temp instance to check for overriding. See usage.
|
||||
*/
|
||||
@Volatile
|
||||
private lateinit var newState: BaseStateImpl
|
||||
private var changingState: KClass<out BaseStateImpl>? = null
|
||||
|
||||
/**
|
||||
* This can only be called by [setState] or in tests. Note:
|
||||
@ -297,32 +294,41 @@ internal abstract class NetworkHandlerSupport(
|
||||
if (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.
|
||||
|
||||
val stateObserver = context.getOrNull(StateObserver)
|
||||
|
||||
val impl = try {
|
||||
new()
|
||||
} catch (e: Throwable) {
|
||||
stateObserver?.exceptionOnCreatingNewState(this, old, e)
|
||||
throw e
|
||||
val changingState = changingState
|
||||
if (changingState != null) {
|
||||
if (changingState == newType) {
|
||||
// no duplicates
|
||||
return null
|
||||
} else {
|
||||
error("New state ${newType.simpleName} clashes with current switching process, changingState = ${changingState.simpleName}.")
|
||||
}
|
||||
}
|
||||
|
||||
// `impl.startState()` may set another state, then this `newState` will be updated.
|
||||
newState = impl
|
||||
this.changingState = newType
|
||||
|
||||
try {
|
||||
check(old !== impl) { "Old and new states cannot be the same." }
|
||||
|
||||
stateObserver?.beforeStateChanged(this, old, impl)
|
||||
val stateObserver = context.getOrNull(StateObserver)
|
||||
|
||||
// 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
|
||||
} catch (e: Throwable) {
|
||||
throw e
|
||||
}
|
||||
val impl = try {
|
||||
new()
|
||||
} catch (e: Throwable) {
|
||||
stateObserver?.exceptionOnCreatingNewState(this, old, e)
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
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
|
||||
} catch (e: Throwable) {
|
||||
throw e
|
||||
}
|
||||
|
||||
val newState = newState
|
||||
if (newState === impl) {
|
||||
// No further change
|
||||
|
||||
_state = impl // update current state
|
||||
@ -333,12 +339,11 @@ internal abstract class NetworkHandlerSupport(
|
||||
|
||||
stateObserver?.stateChanged(this, old, impl) // notify observer
|
||||
_stateChannel.trySend(impl.correspondingState) // notify selector
|
||||
} else {
|
||||
// The newer change prevails, so cancel this.
|
||||
impl.cancel(StateSwitchingException(impl, newState))
|
||||
}
|
||||
|
||||
return@withLock impl
|
||||
return@withLock impl
|
||||
} finally {
|
||||
this.changingState = null
|
||||
}
|
||||
}
|
||||
|
||||
final override suspend fun resumeConnection() {
|
||||
|
Loading…
Reference in New Issue
Block a user