Skip to content

Commit f45ec8d

Browse files
committed
Optimize Empty -> Failing state transition
We can avoid promotion of Empty to NodeList, but create new empty NodeList and capture it in the new Failing instance
1 parent 167c451 commit f45ec8d

File tree

1 file changed

+15
-9
lines changed

1 file changed

+15
-9
lines changed

common/kotlinx-coroutines-core-common/src/JobSupport.kt

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -690,11 +690,10 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
690690
}
691691
is Incomplete -> {
692692
// Not yet finishing -- try to make it failing
693-
val list = tryPromoteToList(state) ?: return@loopOnState
694693
val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
695694
if (state.isActive) {
696695
// active state becomes failing
697-
if (tryMakeFailing(state, list, causeException, cancel)) return true
696+
if (tryMakeFailing(state, causeException, cancel)) return true
698697
} else {
699698
// non active state starts completing
700699
when (tryMakeCompleting(state, createFailure(causeException, cancel), mode = MODE_ATOMIC_DEFAULT)) {
@@ -710,19 +709,26 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
710709
}
711710
}
712711

713-
// Performs promotion of incomplete coroutine state to NodeList, returns null when need to retry
714-
private fun tryPromoteToList(state: Incomplete): NodeList? = state.list ?: null.also {
712+
// Performs promotion of incomplete coroutine state to NodeList for the purpose of
713+
// converting coroutine state to Failing, returns null when need to retry
714+
private fun getOrPromoteFailingList(state: Incomplete): NodeList? = state.list ?:
715715
when (state) {
716-
is Empty -> promoteEmptyToNodeList(state)
717-
is JobNode<*> -> promoteSingleToNodeList(state)
716+
is Empty -> NodeList() // we can allocate new empty list that'll get integrated into Failing state
717+
is JobNode<*> -> {
718+
// SINGLE/SINGLE+ must be promoted to NodeList first, because otherwise we cannot
719+
// correctly capture a reference to it
720+
promoteSingleToNodeList(state)
721+
null // retry
722+
}
718723
else -> error("State should have list: $state")
719724
}
720-
}
721725

722726
// try make new failing state on the condition that we're still in the expected state
723-
private fun tryMakeFailing(state: Incomplete, list: NodeList, rootCause: Throwable, cancel: Boolean): Boolean {
727+
private fun tryMakeFailing(state: Incomplete, rootCause: Throwable, cancel: Boolean): Boolean {
724728
check(state !is Finishing) // only for non-finishing states
725729
check(state.isActive) // only for active states
730+
// get state's list or else promote to list to correctly operate on child lists
731+
val list = getOrPromoteFailingList(state) ?: return false
726732
// Create failing state (with rootCause!)
727733
val failing = Finishing(list, cancel, false, rootCause)
728734
if (!_state.compareAndSet(state, failing)) return false
@@ -782,7 +788,7 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
782788
return COMPLETING_COMPLETED
783789
}
784790
// get state's list or else promote to list to correctly operate on child lists
785-
val list = tryPromoteToList(state) ?: return COMPLETING_RETRY
791+
val list = getOrPromoteFailingList(state) ?: return COMPLETING_RETRY
786792
// promote to Finishing state if we are not in it yet
787793
// This promotion has to be atomic w.r.t to state change, so that a coroutine that is not active yet
788794
// atomically transition to finishing & completing state

0 commit comments

Comments
 (0)