@@ -909,6 +909,10 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
909
909
val child = firstChild(state)
910
910
if (child != null && tryWaitForChild(finishing, child, proposedUpdate))
911
911
return COMPLETING_WAITING_CHILDREN
912
+ list.close(LIST_CHILD_PERMISSION )
913
+ val anotherChild = firstChild(state)
914
+ if (anotherChild != null && tryWaitForChild(finishing, anotherChild, proposedUpdate))
915
+ return COMPLETING_WAITING_CHILDREN
912
916
// otherwise -- we have not children left (all were already cancelled?)
913
917
return finalizeFinishingState(finishing, proposedUpdate)
914
918
}
@@ -938,7 +942,13 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
938
942
val waitChild = lastChild.nextChild()
939
943
// try wait for next child
940
944
if (waitChild != null && tryWaitForChild(state, waitChild, proposedUpdate)) return // waiting for next child
941
- // no more children to wait -- try update state
945
+ // no more children to wait -- stop accepting children
946
+ state.list.close(LIST_CHILD_PERMISSION )
947
+ // did any children get added?
948
+ val waitChildAgain = lastChild.nextChild()
949
+ // try wait for next child
950
+ if (waitChildAgain != null && tryWaitForChild(state, waitChildAgain, proposedUpdate)) return // waiting for next child
951
+ // no more children, now we are sure; try to update the state
942
952
val finalState = finalizeFinishingState(state, proposedUpdate)
943
953
afterCompletion(finalState)
944
954
}
@@ -978,41 +988,45 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
978
988
val node = ChildHandleNode (child).also { it.job = this }
979
989
val added = tryPutNodeIntoList(node) { state, list ->
980
990
if (state is Finishing ) {
981
- val rootCause: Throwable
991
+ val rootCause: Throwable ?
982
992
val handle: ChildHandle
983
993
synchronized(state) {
984
994
// check if we are installing cancellation handler on job that is being cancelled
985
- val maybeRootCause = state.rootCause // != null if cancelling job
995
+ rootCause = state.rootCause // != null if cancelling job
986
996
// We add the node to the list in two cases --- either the job is not being cancelled,
987
997
// or we are adding a child to a coroutine that is not completing yet
988
- if (maybeRootCause == null || ! state.isCompleting) {
998
+ if (rootCause == null || ! state.isCompleting) {
989
999
// Note: add node the list while holding lock on state (make sure it cannot change)
990
- if (! list.addLast(node, LIST_MAX_PERMISSION ))
991
- return @tryPutNodeIntoList false // retry
1000
+ handle = if (list.addLast(node, LIST_CHILD_PERMISSION )) {
1001
+ node
1002
+ } else {
1003
+ NonDisposableHandle
1004
+ }
992
1005
// just return the node if we don't have to invoke the handler (not cancelling yet)
993
- rootCause = maybeRootCause ? : return @tryPutNodeIntoList true
994
1006
// otherwise handler is invoked immediately out of the synchronized section & handle returned
995
- handle = node
996
1007
} else {
997
- rootCause = maybeRootCause
998
1008
handle = NonDisposableHandle
999
1009
}
1000
1010
}
1001
1011
node.invoke(rootCause)
1002
1012
return handle
1003
- } else list.addLast(node, LIST_MAX_PERMISSION ).also { success ->
1004
- if (success) {
1005
- /* * Handling the following case:
1006
- * - A child requested to be added to the list;
1007
- * - We checked the state and saw that it wasn't `Finishing`;
1008
- * - Then, the job got cancelled and notified everyone about it;
1009
- * - Only then did we add the child to the list
1010
- * - and ended up here.
1011
- */
1012
- val latestState = this @JobSupport.state
1013
- if (latestState is Finishing ) {
1014
- synchronized(latestState) { latestState.rootCause }?.let { node.invoke(it) }
1013
+ } else {
1014
+ list.addLast(node, LIST_CHILD_PERMISSION ).also { success ->
1015
+ if (success) {
1016
+ /* * Handling the following case:
1017
+ * - A child requested to be added to the list;
1018
+ * - We checked the state and saw that it wasn't `Finishing`;
1019
+ * - Then, the job got cancelled and notified everyone about it;
1020
+ * - Only then did we add the child to the list
1021
+ * - and ended up here.
1022
+ */
1023
+ val latestState = this @JobSupport.state
1024
+ if (latestState is Finishing ) {
1025
+ synchronized(latestState) { latestState.rootCause }?.let { node.invoke(it) }
1026
+ }
1015
1027
}
1028
+ // if we didn't add the node to the list, we'll loop and notice
1029
+ // either `Finishing` or the final state, so no spin loop here
1016
1030
}
1017
1031
}
1018
1032
}
@@ -1348,6 +1362,7 @@ private val EMPTY_NEW = Empty(false)
1348
1362
private val EMPTY_ACTIVE = Empty (true )
1349
1363
1350
1364
private const val LIST_MAX_PERMISSION = Int .MAX_VALUE
1365
+ private const val LIST_CHILD_PERMISSION = 1
1351
1366
private const val LIST_CANCELLATION_PERMISSION = 0
1352
1367
1353
1368
private class Empty (override val isActive : Boolean ) : Incomplete {
0 commit comments