@@ -899,6 +899,10 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
899
899
val child = firstChild(state)
900
900
if (child != null && tryWaitForChild(finishing, child, proposedUpdate))
901
901
return COMPLETING_WAITING_CHILDREN
902
+ list.close(LIST_CHILD_PERMISSION )
903
+ val anotherChild = firstChild(state)
904
+ if (anotherChild != null && tryWaitForChild(finishing, anotherChild, proposedUpdate))
905
+ return COMPLETING_WAITING_CHILDREN
902
906
// otherwise -- we have not children left (all were already cancelled?)
903
907
return finalizeFinishingState(finishing, proposedUpdate)
904
908
}
@@ -928,7 +932,13 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
928
932
val waitChild = lastChild.nextChild()
929
933
// try wait for next child
930
934
if (waitChild != null && tryWaitForChild(state, waitChild, proposedUpdate)) return // waiting for next child
931
- // no more children to wait -- try update state
935
+ // no more children to wait -- stop accepting children
936
+ state.list.close(LIST_CHILD_PERMISSION )
937
+ // did any children get added?
938
+ val waitChildAgain = lastChild.nextChild()
939
+ // try wait for next child
940
+ if (waitChildAgain != null && tryWaitForChild(state, waitChildAgain, proposedUpdate)) return // waiting for next child
941
+ // no more children, now we are sure; try to update the state
932
942
val finalState = finalizeFinishingState(state, proposedUpdate)
933
943
afterCompletion(finalState)
934
944
}
@@ -968,41 +978,45 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
968
978
val node = ChildHandleNode (child).also { it.job = this }
969
979
val added = tryPutNodeIntoList(node) { state, list ->
970
980
if (state is Finishing ) {
971
- val rootCause: Throwable
981
+ val rootCause: Throwable ?
972
982
val handle: ChildHandle
973
983
synchronized(state) {
974
984
// check if we are installing cancellation handler on job that is being cancelled
975
- val maybeRootCause = state.rootCause // != null if cancelling job
985
+ rootCause = state.rootCause // != null if cancelling job
976
986
// We add the node to the list in two cases --- either the job is not being cancelled,
977
987
// or we are adding a child to a coroutine that is not completing yet
978
- if (maybeRootCause == null || ! state.isCompleting) {
988
+ if (rootCause == null || ! state.isCompleting) {
979
989
// Note: add node the list while holding lock on state (make sure it cannot change)
980
- if (! list.addLast(node, LIST_MAX_PERMISSION ))
981
- return @tryPutNodeIntoList false // retry
990
+ handle = if (list.addLast(node, LIST_CHILD_PERMISSION )) {
991
+ node
992
+ } else {
993
+ NonDisposableHandle
994
+ }
982
995
// just return the node if we don't have to invoke the handler (not cancelling yet)
983
- rootCause = maybeRootCause ? : return @tryPutNodeIntoList true
984
996
// otherwise handler is invoked immediately out of the synchronized section & handle returned
985
- handle = node
986
997
} else {
987
- rootCause = maybeRootCause
988
998
handle = NonDisposableHandle
989
999
}
990
1000
}
991
1001
node.invoke(rootCause)
992
1002
return handle
993
- } else list.addLast(node, LIST_MAX_PERMISSION ).also { success ->
994
- if (success) {
995
- /* * Handling the following case:
996
- * - A child requested to be added to the list;
997
- * - We checked the state and saw that it wasn't `Finishing`;
998
- * - Then, the job got cancelled and notified everyone about it;
999
- * - Only then did we add the child to the list
1000
- * - and ended up here.
1001
- */
1002
- val latestState = this @JobSupport.state
1003
- if (latestState is Finishing ) {
1004
- synchronized(latestState) { latestState.rootCause }?.let { node.invoke(it) }
1003
+ } else {
1004
+ list.addLast(node, LIST_CHILD_PERMISSION ).also { success ->
1005
+ if (success) {
1006
+ /* * Handling the following case:
1007
+ * - A child requested to be added to the list;
1008
+ * - We checked the state and saw that it wasn't `Finishing`;
1009
+ * - Then, the job got cancelled and notified everyone about it;
1010
+ * - Only then did we add the child to the list
1011
+ * - and ended up here.
1012
+ */
1013
+ val latestState = this @JobSupport.state
1014
+ if (latestState is Finishing ) {
1015
+ synchronized(latestState) { latestState.rootCause }?.let { node.invoke(it) }
1016
+ }
1005
1017
}
1018
+ // if we didn't add the node to the list, we'll loop and notice
1019
+ // either `Finishing` or the final state, so no spin loop here
1006
1020
}
1007
1021
}
1008
1022
}
@@ -1340,6 +1354,7 @@ private val EMPTY_NEW = Empty(false)
1340
1354
private val EMPTY_ACTIVE = Empty (true )
1341
1355
1342
1356
private const val LIST_MAX_PERMISSION = Int .MAX_VALUE
1357
+ private const val LIST_CHILD_PERMISSION = 1
1343
1358
private const val LIST_CANCELLATION_PERMISSION = 0
1344
1359
1345
1360
private class Empty (override val isActive : Boolean ) : Incomplete {
0 commit comments