@@ -319,6 +319,31 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
319
319
cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
320
320
}
321
321
322
+ /* *
323
+ * The method that is invoked when the job is cancelled to possibly propagate cancellation to the parent.
324
+ * Returns `true` if the parent is responsible for handling the exception, `false` otherwise.
325
+ *
326
+ * Invariant: never returns `false` for instances of [CancellationException], otherwise such exception
327
+ * may leak to the [CoroutineExceptionHandler].
328
+ */
329
+ private fun cancelParent (cause : Throwable ): Boolean {
330
+ /* CancellationException is considered "normal" and parent usually is not cancelled when child produces it.
331
+ * This allow parent to cancel its children (normally) without being cancelled itself, unless
332
+ * child crashes and produce some other exception during its completion.
333
+ */
334
+ val isCancellation = cause is CancellationException
335
+ val parent = parentHandle
336
+ // No parent -- ignore CE, report other exceptions.
337
+ if (parent == = null || parent == = NonDisposableHandle ) {
338
+ return isCancellation
339
+ }
340
+
341
+ // Is scoped coroutine -- don't propagate, will be rethrown
342
+ if (isScopedCoroutine) return isCancellation
343
+ // Notify parent but don't forget to check cancellation
344
+ return parent.childCancelled(cause) || isCancellation
345
+ }
346
+
322
347
private fun NodeList.notifyCompletion (cause : Throwable ? ) =
323
348
notifyHandlers<JobNode <* >>(this , cause)
324
349
@@ -594,21 +619,29 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
594
619
cancelImpl(parentJob)
595
620
}
596
621
597
- // Child was cancelled with cause
598
- // It is overridden in supervisor implementations to ignore child cancellation
599
- public open fun childCancelled (cause : Throwable ): Boolean =
600
- cancelImpl(cause) && handlesException
622
+ /* *
623
+ * Child was cancelled with a cause.
624
+ * In this method parent decides whether it cancels itself (e.g. on a critical failure) and whether it handles the exception of the child.
625
+ * It is overridden in supervisor implementations to completely ignore any child cancellation.
626
+ * Returns `true` if exception is handled, `false` otherwise (then caller is responsible for handling an exception)
627
+ *
628
+ * Invariant: never returns `false` for instances of [CancellationException], otherwise such exception
629
+ * may leak to the [CoroutineExceptionHandler].
630
+ */
631
+ public open fun childCancelled (cause : Throwable ): Boolean {
632
+ if (cause is CancellationException ) return true
633
+ return cancelImpl(cause) && handlesException
634
+ }
601
635
602
636
/* *
603
637
* Makes this [Job] cancelled with a specified [cause].
604
638
* It is used in [AbstractCoroutine]-derived classes when there is an internal failure.
605
639
*/
606
- public fun cancelCoroutine (cause : Throwable ? ) =
607
- cancelImpl(cause)
640
+ public fun cancelCoroutine (cause : Throwable ? ) = cancelImpl(cause)
608
641
609
642
// cause is Throwable or ParentJob when cancelChild was invoked
610
643
// returns true is exception was handled, false otherwise
611
- private fun cancelImpl (cause : Any? ): Boolean {
644
+ internal fun cancelImpl (cause : Any? ): Boolean {
612
645
if (onCancelComplete) {
613
646
// make sure it is completing, if cancelMakeCompleting returns true it means it had make it
614
647
// completing and had recorded exception
@@ -912,14 +945,12 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
912
945
protected open fun onCancelling (cause : Throwable ? ) {}
913
946
914
947
/* *
915
- * When this function returns `true` the parent is cancelled on cancellation of this job.
916
- * Note that [CancellationException] is considered "normal" and parent is not cancelled when child produces it.
917
- * This allows parent to cancel its children (normally) without being cancelled itself, unless
918
- * child crashes and produce some other exception during its completion.
919
- *
920
- * @suppress **This is unstable API and it is subject to change.*
948
+ * Returns `true` for scoped coroutines.
949
+ * Scoped coroutine is a coroutine that is executed sequentially within the enclosing scope without any concurrency.
950
+ * Scoped coroutines always handle any exception happened within -- they just rethrow it to the enclosing scope.
951
+ * Examples of scoped coroutines are `coroutineScope`, `withTimeout` and `runBlocking`.
921
952
*/
922
- protected open val cancelsParent : Boolean get() = true
953
+ protected open val isScopedCoroutine : Boolean get() = false
923
954
924
955
/* *
925
956
* Returns `true` for jobs that handle their exceptions or integrate them into the job's result via [onCompletionInternal].
@@ -939,20 +970,9 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
939
970
*
940
971
* This method is invoked **exactly once** when the final exception of the job is determined
941
972
* and before it becomes complete. At the moment of invocation the job and all its children are complete.
942
- *
943
- * @suppress **This is unstable API and it is subject to change.*
944
973
*/
945
974
protected open fun handleJobException (exception : Throwable ): Boolean = false
946
975
947
- private fun cancelParent (cause : Throwable ): Boolean {
948
- // CancellationException is considered "normal" and parent is not cancelled when child produces it.
949
- // This allow parent to cancel its children (normally) without being cancelled itself, unless
950
- // child crashes and produce some other exception during its completion.
951
- if (cause is CancellationException ) return true
952
- if (! cancelsParent) return false
953
- return parentHandle?.childCancelled(cause) == true
954
- }
955
-
956
976
/* *
957
977
* Override for completion actions that need to update some external object depending on job's state,
958
978
* right before all the waiters for coroutine's completion are notified.
0 commit comments