@@ -115,11 +115,6 @@ public interface Job : CoroutineContext.Element {
115
115
*/
116
116
public val isCancelled: Boolean
117
117
118
- /* *
119
- * Returns `true` when this job is either [isCancelled] or [isCompleted].
120
- */
121
- public val isCancelledOrCompleted: Boolean
122
-
123
118
/* *
124
119
* Returns the exception that signals the completion of this job -- it returns the original
125
120
* [cancel] cause or an instance of [CancellationException] if this job had completed
@@ -171,35 +166,40 @@ public interface Job : CoroutineContext.Element {
171
166
// ------------ low-level state-notification ------------
172
167
173
168
/* *
174
- * Registers handler that is **synchronously** invoked on cancellation or completion of this job.
175
- * When job is already in _cancelling_ state or is complete for any reason, then the handler
176
- * is immediately invoked with a job's cancellation cause or `null`. Otherwise, handler will be
177
- * invoked once when this job is [cancelled][cancel] or becomes complete.
178
- *
179
- * Unlike [invokeOnCompletion], here the handler is immediately invoked on invocation of [cancel]
180
- * even if the corresponding coroutine has not finished its execution yet.
169
+ * Registers handler that is **synchronously** invoked once on completion of this job.
170
+ * When job is already complete, then the handler is immediately invoked
171
+ * with a job's cancellation cause or `null`. Otherwise, handler will be invoked once when this
172
+ * job is complete.
181
173
*
182
174
* The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] the
183
175
* registration of this handler and release its memory if its invocation is no longer needed.
184
176
* There is no need to dispose the handler after completion of this job. The references to
185
177
* all the handlers are released when this job completes.
186
178
*
179
+ * Note, that the handler is not invoked on invocation of [cancel] when
180
+ * job becomes _cancelling_, but only when the corresponding coroutine had finished execution
181
+ * of its code and became _cancelled_. There is an overloaded version of this function
182
+ * with `onCancelling` parameter to receive notification on _cancelling_ state.
183
+ *
187
184
* **Note**: This function is a part of internal machinery that supports parent-child hierarchies
188
185
* and allows for implementation of suspending functions that wait on the Job's state.
189
186
* This function should not be used in general application code.
190
- * Implementations of `CompletionHandler` must be fast and _lock-free_
187
+ * Implementations of `CompletionHandler` must be fast and _lock-free_.
191
188
*/
192
- public fun invokeOnCancellation (handler : CompletionHandler ): DisposableHandle
189
+ public fun invokeOnCompletion (handler : CompletionHandler ): DisposableHandle
193
190
194
191
/* *
195
- * Registers handler that is **synchronously** invoked on completion of this job.
192
+ * Registers handler that is **synchronously** invoked once on cancellation or completion of this job.
196
193
* When job is already complete, then the handler is immediately invoked
197
194
* with a job's cancellation cause or `null`. Otherwise, handler will be invoked once when this
198
- * job is complete.
195
+ * job is cancelled or complete.
199
196
*
200
- * Unlike [invokeOnCancellation], here the handler is not invoked on invocation of [cancel] when
201
- * job becomes _cancelling_, but only when the corresponding coroutine had finished execution
202
- * of its code and became _cancelled_.
197
+ * Invocation of this handler on a transition to a transient _cancelling_ state
198
+ * is controlled by [onCancelling] boolean parameter.
199
+ * The handler is invoked on invocation of [cancel] when
200
+ * job becomes _cancelling_ when [onCancelling] parameters is set to `true`. However,
201
+ * when this [Job] is not backed by a coroutine, like [CompletableDeferred] or [CancellableContinuation]
202
+ * (both of which do not posses a _cancelling_ state), then the value of [onCancelling] parameter is ignored.
203
203
*
204
204
* The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] the
205
205
* registration of this handler and release its memory if its invocation is no longer needed.
@@ -209,9 +209,9 @@ public interface Job : CoroutineContext.Element {
209
209
* **Note**: This function is a part of internal machinery that supports parent-child hierarchies
210
210
* and allows for implementation of suspending functions that wait on the Job's state.
211
211
* This function should not be used in general application code.
212
- * Implementations of `CompletionHandler` must be fast and _lock-free_
212
+ * Implementations of `CompletionHandler` must be fast and _lock-free_.
213
213
*/
214
- public fun invokeOnCompletion (handler : CompletionHandler ): DisposableHandle
214
+ public fun invokeOnCompletion (handler : CompletionHandler , onCancelling : Boolean ): DisposableHandle
215
215
216
216
// ------------ unstable internal API ------------
217
217
@@ -272,12 +272,12 @@ public interface DisposableHandle : Job.Registration {
272
272
}
273
273
274
274
/* *
275
- * Handler for [Job.invokeOnCompletion] and [Job.invokeOnCancellation] .
275
+ * Handler for [Job.invokeOnCompletion].
276
276
*
277
277
* **Note**: This type is a part of internal machinery that supports parent-child hierarchies
278
278
* and allows for implementation of suspending functions that wait on the Job's state.
279
279
* This type should not be used in general application code.
280
- * Implementations of `CompletionHandler` must be fast and _lock-free_
280
+ * Implementations of `CompletionHandler` must be fast and _lock-free_.
281
281
*/
282
282
public typealias CompletionHandler = (Throwable ? ) -> Unit
283
283
@@ -441,13 +441,19 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
441
441
parentHandle = NonDisposableHandle
442
442
return
443
443
}
444
+ parent.start() // make sure the parent is started
444
445
// directly pass HandlerNode to parent scope to optimize one closure object (see makeNode)
445
- val newRegistration = parent.invokeOnCancellation (ParentOnCancellation (parent, this ) )
446
+ val newRegistration = parent.invokeOnCompletion (ParentOnCancellation (parent), onCancelling = true )
446
447
parentHandle = newRegistration
447
448
// now check our state _after_ registering (see updateState order of actions)
448
449
if (isCompleted) newRegistration.dispose()
449
450
}
450
451
452
+ private inner class ParentOnCancellation (parent : Job ) : JobCancellationNode<Job>(parent) {
453
+ override fun invokeOnce (reason : Throwable ? ) { onParentCancellation(reason) }
454
+ override fun toString (): String = " ParentOnCancellation[${this @JobSupport} ]"
455
+ }
456
+
451
457
/* *
452
458
* Invoked at most once on parent completion.
453
459
* @suppress **This is unstable API and it is subject to change.**
@@ -490,11 +496,6 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
490
496
return state is Cancelled || state is Cancelling
491
497
}
492
498
493
- public final override val isCancelledOrCompleted: Boolean get() {
494
- val state = this .state
495
- return state !is Incomplete || state is Cancelling
496
- }
497
-
498
499
// ------------ state update ------------
499
500
500
501
/* *
@@ -637,20 +638,20 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
637
638
}
638
639
}
639
640
640
- public final override fun invokeOnCancellation (handler : CompletionHandler ): DisposableHandle =
641
- installHandler(handler, onCancellation = hasCancellingState)
642
-
643
641
public final override fun invokeOnCompletion (handler : CompletionHandler ): DisposableHandle =
644
- installHandler(handler, onCancellation = false )
642
+ installHandler(handler, onCancelling = false )
643
+
644
+ public final override fun invokeOnCompletion (handler : CompletionHandler , onCancelling : Boolean ): DisposableHandle =
645
+ installHandler(handler, onCancelling = onCancelling && hasCancellingState)
645
646
646
- private fun installHandler (handler : CompletionHandler , onCancellation : Boolean ): DisposableHandle {
647
+ private fun installHandler (handler : CompletionHandler , onCancelling : Boolean ): DisposableHandle {
647
648
var nodeCache: JobNode <* >? = null
648
649
lockFreeLoopOnState { state ->
649
650
when (state) {
650
651
is Empty -> { // EMPTY_X state -- no completion handlers
651
652
if (state.isActive) {
652
653
// try move to SINGLE state
653
- val node = nodeCache ? : makeNode(handler, onCancellation ).also { nodeCache = it }
654
+ val node = nodeCache ? : makeNode(handler, onCancelling ).also { nodeCache = it }
654
655
if (STATE .compareAndSet(this , state, node)) return node
655
656
} else
656
657
promoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine
@@ -659,15 +660,15 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
659
660
promoteSingleToNodeList(state)
660
661
}
661
662
is NodeList -> { // LIST -- a list of completion handlers (either new or active)
662
- val node = nodeCache ? : makeNode(handler, onCancellation ).also { nodeCache = it }
663
+ val node = nodeCache ? : makeNode(handler, onCancelling ).also { nodeCache = it }
663
664
if (addLastAtomic(state, state, node)) return node
664
665
}
665
666
is Cancelling -> { // CANCELLING -- has a list of completion handlers
666
- if (onCancellation ) { // installing cancellation handler on job that is being cancelled
667
+ if (onCancelling ) { // installing cancellation handler on job that is being cancelled
667
668
handler((state as ? CompletedExceptionally )?.exception)
668
669
return NonDisposableHandle
669
670
}
670
- val node = nodeCache ? : makeNode(handler, onCancellation ).also { nodeCache = it }
671
+ val node = nodeCache ? : makeNode(handler, onCancelling ).also { nodeCache = it }
671
672
if (addLastAtomic(state, state.list, node)) return node
672
673
}
673
674
else -> { // is inactive
@@ -678,8 +679,8 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
678
679
}
679
680
}
680
681
681
- private fun makeNode (handler : CompletionHandler , onCancellation : Boolean ): JobNode <* > =
682
- if (onCancellation )
682
+ private fun makeNode (handler : CompletionHandler , onCancelling : Boolean ): JobNode <* > =
683
+ if (onCancelling )
683
684
(handler as ? JobCancellationNode <* >)?.also { require(it.job == = this ) }
684
685
? : InvokeOnCancellation (this , handler)
685
686
else
@@ -816,14 +817,15 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
816
817
817
818
/* *
818
819
* Override to process any exceptions that were encountered while invoking completion handlers
819
- * installed via [invokeOnCancellation] or [ invokeOnCompletion].
820
+ * installed via [invokeOnCompletion].
820
821
*/
821
822
protected open fun handleException (exception : Throwable ) {
822
823
throw exception
823
824
}
824
825
825
826
/* *
826
- * It is invoked once when job is cancelled or is completed, similarly to [invokeOnCancellation].
827
+ * It is invoked once when job is cancelled or is completed, similarly to [invokeOnCompletion] with
828
+ * `onCancelling` set to `true`.
827
829
*/
828
830
protected open fun onCancellation () {}
829
831
@@ -912,14 +914,6 @@ public open class JobSupport(active: Boolean) : AbstractCoroutineContextElement(
912
914
) : CompletedExceptionally(cause)
913
915
914
916
915
- private class ParentOnCancellation (
916
- parentJob : Job ,
917
- private val subordinateJob : JobSupport
918
- ) : JobCancellationNode<Job>(parentJob) {
919
- override fun invokeOnce (reason : Throwable ? ) { subordinateJob.onParentCancellation(reason) }
920
- override fun toString (): String = " ParentOnCancellation[$subordinateJob ]"
921
- }
922
-
923
917
/*
924
918
* =================================================================================================
925
919
* This is ready-to-use implementation for Deferred interface.
0 commit comments