@@ -879,7 +879,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
879
879
if (finishing.isCompleting) return COMPLETING_ALREADY
880
880
// mark as completing
881
881
finishing.isCompleting = true
882
- // if we need to promote to finishing then atomically do it here.
882
+ // if we need to promote to finishing, then atomically do it here.
883
883
// We do it as early is possible while still holding the lock. This ensures that we cancelImpl asap
884
884
// (if somebody else is faster) and we synchronize all the threads on this finishing lock asap.
885
885
if (finishing != = state) {
@@ -893,7 +893,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
893
893
// If it just becomes cancelling --> must process cancelling notifications
894
894
notifyRootCause = finishing.rootCause.takeIf { ! wasCancelling }
895
895
}
896
- // process cancelling notification here -- it cancels all the children _before_ we start to to wait them (sic!!!)
896
+ // process cancelling notification here -- it cancels all the children _before_ we start to wait them (sic!!!)
897
897
notifyRootCause?.let { notifyCancelling(list, it) }
898
898
// now wait for children
899
899
val child = firstChild(state)
@@ -976,47 +976,39 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
976
976
* but the parent *will* wait for that child before completion and will handle its exception.
977
977
*/
978
978
val node = ChildHandleNode (child).also { it.job = this }
979
- val added = tryPutNodeIntoList(node) { state, list ->
980
- if (state is Finishing ) {
981
- val rootCause: Throwable ?
982
- val handle: ChildHandle
983
- synchronized(state) {
984
- // check if we are installing cancellation handler on job that is being cancelled
985
- rootCause = state.rootCause // != null if cancelling job
986
- // We add the node to the list in two cases --- either the job is not being cancelled,
987
- // or we are adding a child to a coroutine that is not completing yet
988
- if (rootCause == null || ! state.isCompleting) {
989
- // Note: add node the list while holding lock on state (make sure it cannot change)
990
- handle = if (list.addLast(node, LIST_CHILD_PERMISSION )) {
991
- node
992
- } else {
993
- NonDisposableHandle
994
- }
995
- // just return the node if we don't have to invoke the handler (not cancelling yet)
996
- // otherwise handler is invoked immediately out of the synchronized section & handle returned
997
- } else {
998
- handle = NonDisposableHandle
999
- }
1000
- }
1001
- node.invoke(rootCause)
1002
- return handle
979
+ val added = tryPutNodeIntoList(node) { _, list ->
980
+ // First, try to add a child along the cancellation handlers
981
+ val addedBeforeCancellation = list.addLast(node, LIST_CANCELLATION_PERMISSION )
982
+ if (addedBeforeCancellation) {
983
+ // The child managed to be added before the parent started to cancel or complete. Success.
984
+ true
1003
985
} 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
- }
986
+ // Either cancellation or completion already happened, the child was not added.
987
+ // Now we need to try adding it for completion.
988
+ val addedBeforeCompletion = list.addLast(node, LIST_CHILD_PERMISSION )
989
+ // Whether or not we managed to add the child before the parent completed, we need to investigate:
990
+ // why didn't we manage to add it before cancellation?
991
+ // If it's because cancellation happened in the meantime, we need to notify the child.
992
+ // We check the latest state because the original state with which we started may not have had
993
+ // the information about the cancellation yet.
994
+ val rootCause = when (val latestState = this .state) {
995
+ is Finishing -> {
996
+ // The state is still incomplete, so we need to notify the child about the completion cause.
997
+ synchronized(latestState) { latestState.rootCause }
998
+ }
999
+ else -> {
1000
+ // Since the list is already closed for `onCancelling`, the job is either Finishing or
1001
+ // already completed. We need to notify the child about the completion cause.
1002
+ assert { latestState !is Incomplete }
1003
+ (latestState as ? CompletedExceptionally )?.cause
1017
1004
}
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
1005
+ }
1006
+ if (addedBeforeCompletion) {
1007
+ if (rootCause != null ) node.invoke(rootCause)
1008
+ true
1009
+ } else {
1010
+ node.invoke(rootCause)
1011
+ return NonDisposableHandle
1020
1012
}
1021
1013
}
1022
1014
}
0 commit comments