blob: a3070213d1a7114fb70e057c2c2e5bc8d4c2e666 [file] [log] [blame] [view]
Roman Elizarov660c2d72020-02-14 13:18:37 +03001<!--- TEST_NAME ExceptionsGuideTest -->
hadihariri7db55532018-09-15 10:35:08 +02002
Prendotab8a559d2018-11-30 16:24:23 +03003**Table of contents**
hadihariri7db55532018-09-15 10:35:08 +02004
5<!--- TOC -->
6
Roman Elizarov3258e1f2019-08-22 20:08:48 +03007* [Exception Handling](#exception-handling)
hadihariri7db55532018-09-15 10:35:08 +02008 * [Exception propagation](#exception-propagation)
9 * [CoroutineExceptionHandler](#coroutineexceptionhandler)
10 * [Cancellation and exceptions](#cancellation-and-exceptions)
11 * [Exceptions aggregation](#exceptions-aggregation)
Roman Elizarov3258e1f2019-08-22 20:08:48 +030012 * [Supervision](#supervision)
13 * [Supervision job](#supervision-job)
14 * [Supervision scope](#supervision-scope)
15 * [Exceptions in supervised coroutines](#exceptions-in-supervised-coroutines)
hadihariri7db55532018-09-15 10:35:08 +020016
Roman Elizarov660c2d72020-02-14 13:18:37 +030017<!--- END -->
hadihariri7db55532018-09-15 10:35:08 +020018
Roman Elizarov3258e1f2019-08-22 20:08:48 +030019## Exception Handling
hadihariri7db55532018-09-15 10:35:08 +020020
hadihariri7db55532018-09-15 10:35:08 +020021This section covers exception handling and cancellation on exceptions.
Masood Fallahpoor4c28f942020-10-01 18:57:55 +033022We already know that a cancelled coroutine throws [CancellationException] in suspension points and that it
Roman Elizarov94970df2020-04-22 13:58:16 +030023is ignored by the coroutines' machinery. Here we look at what happens if an exception is thrown during cancellation or multiple children of the same
24coroutine throw an exception.
hadihariri7db55532018-09-15 10:35:08 +020025
26### Exception propagation
27
Roman Elizarov94970df2020-04-22 13:58:16 +030028Coroutine builders come in two flavors: propagating exceptions automatically ([launch] and [actor]) or
hadihariri7db55532018-09-15 10:35:08 +020029exposing them to users ([async] and [produce]).
Roman Elizarov94970df2020-04-22 13:58:16 +030030When these builders are used to create a _root_ coroutine, that is not a _child_ of another coroutine,
Yanis Baturaf4fb2042020-06-21 20:03:40 +070031the former builders treat exceptions as **uncaught** exceptions, similar to Java's `Thread.uncaughtExceptionHandler`,
Roman Elizarov94970df2020-04-22 13:58:16 +030032while the latter are relying on the user to consume the final
hadihariri7db55532018-09-15 10:35:08 +020033exception, for example via [await][Deferred.await] or [receive][ReceiveChannel.receive]
Alexander Prendotacbeef102018-09-27 18:42:04 +030034([produce] and [receive][ReceiveChannel.receive] are covered later in [Channels](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/channels.md) section).
hadihariri7db55532018-09-15 10:35:08 +020035
Roman Elizarov94970df2020-04-22 13:58:16 +030036It can be demonstrated by a simple example that creates root coroutines using the [GlobalScope]:
hadihariri7db55532018-09-15 10:35:08 +020037
Alexander Prendotacbeef102018-09-27 18:42:04 +030038<div class="sample" markdown="1" theme="idea" data-highlight-only>
39
hadihariri7db55532018-09-15 10:35:08 +020040```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +030041import kotlinx.coroutines.*
42
43fun main() = runBlocking {
Roman Elizarov94970df2020-04-22 13:58:16 +030044 val job = GlobalScope.launch { // root coroutine with launch
hadihariri7db55532018-09-15 10:35:08 +020045 println("Throwing exception from launch")
46 throw IndexOutOfBoundsException() // Will be printed to the console by Thread.defaultUncaughtExceptionHandler
47 }
48 job.join()
49 println("Joined failed job")
Roman Elizarov94970df2020-04-22 13:58:16 +030050 val deferred = GlobalScope.async { // root coroutine with async
hadihariri7db55532018-09-15 10:35:08 +020051 println("Throwing exception from async")
52 throw ArithmeticException() // Nothing is printed, relying on user to call await
53 }
54 try {
55 deferred.await()
56 println("Unreached")
57 } catch (e: ArithmeticException) {
58 println("Caught ArithmeticException")
59 }
60}
61```
62
Alexander Prendotacbeef102018-09-27 18:42:04 +030063</div>
64
Adam Howardf13549a2020-06-02 11:17:46 +010065> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt).
hadihariri7db55532018-09-15 10:35:08 +020066
Alexander Prendotacbeef102018-09-27 18:42:04 +030067The output of this code is (with [debug](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/coroutine-context-and-dispatchers.md#debugging-coroutines-and-threads)):
hadihariri7db55532018-09-15 10:35:08 +020068
69```text
70Throwing exception from launch
Roman Elizarov303708b2018-09-28 12:20:49 +030071Exception in thread "DefaultDispatcher-worker-2 @coroutine#2" java.lang.IndexOutOfBoundsException
hadihariri7db55532018-09-15 10:35:08 +020072Joined failed job
73Throwing exception from async
74Caught ArithmeticException
75```
76
77<!--- TEST EXCEPTION-->
78
79### CoroutineExceptionHandler
80
Roman Elizarov94970df2020-04-22 13:58:16 +030081It is possible to customize the default behavior of printing **uncaught** exceptions to the console.
82[CoroutineExceptionHandler] context element on a _root_ coroutine can be used as generic `catch` block for
83this root coroutine and all its children where custom exception handling may take place.
84It is similar to [`Thread.uncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)).
85You cannot recover from the exception in the `CoroutineExceptionHandler`. The coroutine had already completed
86with the corresponding exception when the handler is called. Normally, the handler is used to
87log the exception, show some kind of error message, terminate, and/or restart the application.
hadihariri7db55532018-09-15 10:35:08 +020088
89On JVM it is possible to redefine global exception handler for all coroutines by registering [CoroutineExceptionHandler] via
90[`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html).
91Global exception handler is similar to
92[`Thread.defaultUncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler))
93which is used when no more specific handlers are registered.
94On Android, `uncaughtExceptionPreHandler` is installed as a global coroutine exception handler.
95
Roman Elizarov94970df2020-04-22 13:58:16 +030096`CoroutineExceptionHandler` is invoked only on **uncaught** exceptions &mdash; exceptions that were not handled in any other way.
97In particular, all _children_ coroutines (coroutines created in the context of another [Job]) delegate handling of
98their exceptions to their parent coroutine, which also delegates to the parent, and so on until the root,
99so the `CoroutineExceptionHandler` installed in their context is never used.
100In addition to that, [async] builder always catches all exceptions and represents them in the resulting [Deferred] object,
101so its `CoroutineExceptionHandler` has no effect either.
102
103> Coroutines running in supervision scope do not propagate exceptions to their parent and are
104excluded from this rule. A further [Supervision](#supervision) section of this document gives more details.
hadihariri7db55532018-09-15 10:35:08 +0200105
Prendota65e6c8c2018-10-17 11:51:08 +0300106<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300107
hadihariri7db55532018-09-15 10:35:08 +0200108```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300109import kotlinx.coroutines.*
110
111fun main() = runBlocking {
112//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200113 val handler = CoroutineExceptionHandler { _, exception ->
Roman Elizarov94970df2020-04-22 13:58:16 +0300114 println("CoroutineExceptionHandler got $exception")
hadihariri7db55532018-09-15 10:35:08 +0200115 }
Roman Elizarov94970df2020-04-22 13:58:16 +0300116 val job = GlobalScope.launch(handler) { // root coroutine, running in GlobalScope
hadihariri7db55532018-09-15 10:35:08 +0200117 throw AssertionError()
118 }
Roman Elizarov94970df2020-04-22 13:58:16 +0300119 val deferred = GlobalScope.async(handler) { // also root, but async instead of launch
hadihariri7db55532018-09-15 10:35:08 +0200120 throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await()
121 }
122 joinAll(job, deferred)
Prendota65e6c8c2018-10-17 11:51:08 +0300123//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200124}
125```
126
Alexander Prendotacbeef102018-09-27 18:42:04 +0300127</div>
128
Adam Howardf13549a2020-06-02 11:17:46 +0100129> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt).
hadihariri7db55532018-09-15 10:35:08 +0200130
131The output of this code is:
132
133```text
Roman Elizarov94970df2020-04-22 13:58:16 +0300134CoroutineExceptionHandler got java.lang.AssertionError
hadihariri7db55532018-09-15 10:35:08 +0200135```
136
137<!--- TEST-->
138
139### Cancellation and exceptions
140
Roman Elizarov94970df2020-04-22 13:58:16 +0300141Cancellation is closely related to exceptions. Coroutines internally use `CancellationException` for cancellation, these
hadihariri7db55532018-09-15 10:35:08 +0200142exceptions are ignored by all handlers, so they should be used only as the source of additional debug information, which can
143be obtained by `catch` block.
Marek Langiewiczb6dd65c2019-10-18 12:10:27 +0200144When a coroutine is cancelled using [Job.cancel], it terminates, but it does not cancel its parent.
hadihariri7db55532018-09-15 10:35:08 +0200145
Prendota65e6c8c2018-10-17 11:51:08 +0300146<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300147
hadihariri7db55532018-09-15 10:35:08 +0200148```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300149import kotlinx.coroutines.*
150
151fun main() = runBlocking {
152//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200153 val job = launch {
154 val child = launch {
155 try {
156 delay(Long.MAX_VALUE)
157 } finally {
158 println("Child is cancelled")
159 }
160 }
161 yield()
162 println("Cancelling child")
163 child.cancel()
164 child.join()
165 yield()
166 println("Parent is not cancelled")
167 }
168 job.join()
Prendota65e6c8c2018-10-17 11:51:08 +0300169//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200170}
171```
172
Alexander Prendotacbeef102018-09-27 18:42:04 +0300173</div>
174
Adam Howardf13549a2020-06-02 11:17:46 +0100175> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt).
hadihariri7db55532018-09-15 10:35:08 +0200176
177The output of this code is:
178
179```text
180Cancelling child
181Child is cancelled
182Parent is not cancelled
183```
184
185<!--- TEST-->
186
andrethielef605b262019-10-03 12:18:37 +0200187If a coroutine encounters an exception other than `CancellationException`, it cancels its parent with that exception.
hadihariri7db55532018-09-15 10:35:08 +0200188This behaviour cannot be overridden and is used to provide stable coroutines hierarchies for
Roman Elizarov94970df2020-04-22 13:58:16 +0300189[structured concurrency](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/composing-suspending-functions.md#structured-concurrency-with-async).
190[CoroutineExceptionHandler] implementation is not used for child coroutines.
hadihariri7db55532018-09-15 10:35:08 +0200191
Roman Elizarov94970df2020-04-22 13:58:16 +0300192> In these examples [CoroutineExceptionHandler] is always installed to a coroutine
hadihariri7db55532018-09-15 10:35:08 +0200193that is created in [GlobalScope]. It does not make sense to install an exception handler to a coroutine that
194is launched in the scope of the main [runBlocking], since the main coroutine is going to be always cancelled
195when its child completes with exception despite the installed handler.
196
Roman Elizarov94970df2020-04-22 13:58:16 +0300197The original exception is handled by the parent only when all its children terminate,
198which is demonstrated by the following example.
199
Prendota65e6c8c2018-10-17 11:51:08 +0300200<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300201
hadihariri7db55532018-09-15 10:35:08 +0200202```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300203import kotlinx.coroutines.*
204
205fun main() = runBlocking {
巳月55a48fb2018-10-31 18:27:47 +0800206//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200207 val handler = CoroutineExceptionHandler { _, exception ->
Roman Elizarov94970df2020-04-22 13:58:16 +0300208 println("CoroutineExceptionHandler got $exception")
hadihariri7db55532018-09-15 10:35:08 +0200209 }
210 val job = GlobalScope.launch(handler) {
211 launch { // the first child
212 try {
213 delay(Long.MAX_VALUE)
214 } finally {
215 withContext(NonCancellable) {
216 println("Children are cancelled, but exception is not handled until all children terminate")
217 delay(100)
218 println("The first child finished its non cancellable block")
219 }
220 }
221 }
222 launch { // the second child
223 delay(10)
224 println("Second child throws an exception")
225 throw ArithmeticException()
226 }
227 }
228 job.join()
Prendota65e6c8c2018-10-17 11:51:08 +0300229//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200230}
231```
232
Alexander Prendotacbeef102018-09-27 18:42:04 +0300233</div>
234
Adam Howardf13549a2020-06-02 11:17:46 +0100235> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt).
hadihariri7db55532018-09-15 10:35:08 +0200236
237The output of this code is:
238
239```text
240Second child throws an exception
241Children are cancelled, but exception is not handled until all children terminate
242The first child finished its non cancellable block
Roman Elizarov94970df2020-04-22 13:58:16 +0300243CoroutineExceptionHandler got java.lang.ArithmeticException
hadihariri7db55532018-09-15 10:35:08 +0200244```
245<!--- TEST-->
246
247### Exceptions aggregation
248
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700249When multiple children of a coroutine fail with an exception, the
Roman Elizarov94970df2020-04-22 13:58:16 +0300250general rule is "the first exception wins", so the first exception gets handled.
251All additional exceptions that happen after the first one are attached to the first exception as suppressed ones.
Prendota65e6c8c2018-10-17 11:51:08 +0300252
hadihariri7db55532018-09-15 10:35:08 +0200253<!--- INCLUDE
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300254import kotlinx.coroutines.exceptions.*
hadihariri7db55532018-09-15 10:35:08 +0200255-->
256
Prendota65e6c8c2018-10-17 11:51:08 +0300257<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300258
hadihariri7db55532018-09-15 10:35:08 +0200259```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300260import kotlinx.coroutines.*
261import java.io.*
262
263fun main() = runBlocking {
hadihariri7db55532018-09-15 10:35:08 +0200264 val handler = CoroutineExceptionHandler { _, exception ->
Roman Elizarov94970df2020-04-22 13:58:16 +0300265 println("CoroutineExceptionHandler got $exception with suppressed ${exception.suppressed.contentToString()}")
hadihariri7db55532018-09-15 10:35:08 +0200266 }
267 val job = GlobalScope.launch(handler) {
268 launch {
269 try {
Roman Elizarov94970df2020-04-22 13:58:16 +0300270 delay(Long.MAX_VALUE) // it gets cancelled when another sibling fails with IOException
hadihariri7db55532018-09-15 10:35:08 +0200271 } finally {
Roman Elizarov94970df2020-04-22 13:58:16 +0300272 throw ArithmeticException() // the second exception
hadihariri7db55532018-09-15 10:35:08 +0200273 }
274 }
275 launch {
Roman Elizarov938c5e92018-09-28 16:10:09 +0300276 delay(100)
Roman Elizarov94970df2020-04-22 13:58:16 +0300277 throw IOException() // the first exception
hadihariri7db55532018-09-15 10:35:08 +0200278 }
279 delay(Long.MAX_VALUE)
280 }
Prendota65e6c8c2018-10-17 11:51:08 +0300281 job.join()
hadihariri7db55532018-09-15 10:35:08 +0200282}
283```
284
Alexander Prendotacbeef102018-09-27 18:42:04 +0300285</div>
286
Adam Howardf13549a2020-06-02 11:17:46 +0100287> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt).
hadihariri7db55532018-09-15 10:35:08 +0200288
Roman Elizarov54617b72018-09-28 17:42:44 +0300289> Note: This above code will work properly only on JDK7+ that supports `suppressed` exceptions
290
hadihariri7db55532018-09-15 10:35:08 +0200291The output of this code is:
292
293```text
Roman Elizarov94970df2020-04-22 13:58:16 +0300294CoroutineExceptionHandler got java.io.IOException with suppressed [java.lang.ArithmeticException]
hadihariri7db55532018-09-15 10:35:08 +0200295```
296
297<!--- TEST-->
298
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700299> Note that this mechanism currently only works on Java version 1.7+.
300The JS and Native restrictions are temporary and will be lifted in the future.
hadihariri7db55532018-09-15 10:35:08 +0200301
Roman Elizarov94970df2020-04-22 13:58:16 +0300302Cancellation exceptions are transparent and are unwrapped by default:
hadihariri7db55532018-09-15 10:35:08 +0200303
Prendota65e6c8c2018-10-17 11:51:08 +0300304<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300305
hadihariri7db55532018-09-15 10:35:08 +0200306```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300307import kotlinx.coroutines.*
308import java.io.*
309
310fun main() = runBlocking {
311//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200312 val handler = CoroutineExceptionHandler { _, exception ->
Roman Elizarov94970df2020-04-22 13:58:16 +0300313 println("CoroutineExceptionHandler got $exception")
hadihariri7db55532018-09-15 10:35:08 +0200314 }
315 val job = GlobalScope.launch(handler) {
Roman Elizarov94970df2020-04-22 13:58:16 +0300316 val inner = launch { // all this stack of coroutines will get cancelled
hadihariri7db55532018-09-15 10:35:08 +0200317 launch {
318 launch {
Roman Elizarov94970df2020-04-22 13:58:16 +0300319 throw IOException() // the original exception
hadihariri7db55532018-09-15 10:35:08 +0200320 }
321 }
322 }
323 try {
324 inner.join()
Vsevolod Tolstopyatova2d80882018-09-24 19:51:49 +0300325 } catch (e: CancellationException) {
326 println("Rethrowing CancellationException with original cause")
Roman Elizarov94970df2020-04-22 13:58:16 +0300327 throw e // cancellation exception is rethrown, yet the original IOException gets to the handler
hadihariri7db55532018-09-15 10:35:08 +0200328 }
329 }
330 job.join()
Prendota65e6c8c2018-10-17 11:51:08 +0300331//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200332}
333```
334
Alexander Prendotacbeef102018-09-27 18:42:04 +0300335</div>
336
Adam Howardf13549a2020-06-02 11:17:46 +0100337> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt).
hadihariri7db55532018-09-15 10:35:08 +0200338
339The output of this code is:
340
341```text
Vsevolod Tolstopyatova2d80882018-09-24 19:51:49 +0300342Rethrowing CancellationException with original cause
Roman Elizarov94970df2020-04-22 13:58:16 +0300343CoroutineExceptionHandler got java.io.IOException
hadihariri7db55532018-09-15 10:35:08 +0200344```
345<!--- TEST-->
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300346
Roman Elizarov3258e1f2019-08-22 20:08:48 +0300347### Supervision
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300348
349As we have studied before, cancellation is a bidirectional relationship propagating through the whole
Roman Elizarov94970df2020-04-22 13:58:16 +0300350hierarchy of coroutines. Let us take a look at the case when unidirectional cancellation is required.
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300351
Aaron Stacy2fe443b2019-03-24 20:00:36 -0500352A good example of such a requirement is a UI component with the job defined in its scope. If any of the UI's child tasks
353have failed, it is not always necessary to cancel (effectively kill) the whole UI component,
Roman Elizarov94970df2020-04-22 13:58:16 +0300354but if UI component is destroyed (and its job is cancelled), then it is necessary to fail all child jobs as their results are no longer needed.
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300355
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700356Another example is a server process that spawns multiple child jobs and needs to _supervise_
357their execution, tracking their failures and only restarting the failed ones.
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300358
Roman Elizarov3258e1f2019-08-22 20:08:48 +0300359#### Supervision job
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300360
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700361The [SupervisorJob][SupervisorJob()] can be used for these purposes.
Roman Elizarov94970df2020-04-22 13:58:16 +0300362It is similar to a regular [Job][Job()] with the only exception that cancellation is propagated
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700363only downwards. This can easily be demonstrated using the following example:
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300364
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300365<div class="sample" markdown="1" theme="idea" data-highlight-only>
366
367```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300368import kotlinx.coroutines.*
369
370fun main() = runBlocking {
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300371 val supervisor = SupervisorJob()
372 with(CoroutineScope(coroutineContext + supervisor)) {
Vadym Od9819662018-10-19 16:16:57 -0500373 // launch the first child -- its exception is ignored for this example (don't do this in practice!)
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300374 val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) {
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700375 println("The first child is failing")
376 throw AssertionError("The first child is cancelled")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300377 }
378 // launch the second child
379 val secondChild = launch {
380 firstChild.join()
381 // Cancellation of the first child is not propagated to the second child
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700382 println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300383 try {
384 delay(Long.MAX_VALUE)
385 } finally {
386 // But cancellation of the supervisor is propagated
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700387 println("The second child is cancelled because the supervisor was cancelled")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300388 }
389 }
390 // wait until the first child fails & completes
391 firstChild.join()
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700392 println("Cancelling the supervisor")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300393 supervisor.cancel()
394 secondChild.join()
395 }
396}
397```
398
399</div>
400
Adam Howardf13549a2020-06-02 11:17:46 +0100401> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-supervision-01.kt).
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300402
403The output of this code is:
404
405```text
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700406The first child is failing
407The first child is cancelled: true, but the second one is still active
408Cancelling the supervisor
409The second child is cancelled because the supervisor was cancelled
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300410```
411<!--- TEST-->
412
413
Roman Elizarov3258e1f2019-08-22 20:08:48 +0300414#### Supervision scope
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300415
Roman Elizaroveb1b5db2020-10-16 10:45:08 +0300416Instead of [coroutineScope][_coroutineScope], we can use [supervisorScope][_supervisorScope] for _scoped_ concurrency. It propagates the cancellation
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700417in one direction only and cancels all its children only if it failed itself. It also waits for all children before completion
Roman Elizaroveb1b5db2020-10-16 10:45:08 +0300418just like [coroutineScope][_coroutineScope] does.
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300419
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300420<div class="sample" markdown="1" theme="idea" data-highlight-only>
421
422```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300423import kotlin.coroutines.*
424import kotlinx.coroutines.*
425
426fun main() = runBlocking {
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300427 try {
428 supervisorScope {
429 val child = launch {
430 try {
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700431 println("The child is sleeping")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300432 delay(Long.MAX_VALUE)
433 } finally {
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700434 println("The child is cancelled")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300435 }
436 }
437 // Give our child a chance to execute and print using yield
438 yield()
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700439 println("Throwing an exception from the scope")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300440 throw AssertionError()
441 }
442 } catch(e: AssertionError) {
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700443 println("Caught an assertion error")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300444 }
445}
446```
447
448</div>
449
Adam Howardf13549a2020-06-02 11:17:46 +0100450> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-supervision-02.kt).
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300451
452The output of this code is:
453
454```text
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700455The child is sleeping
456Throwing an exception from the scope
457The child is cancelled
458Caught an assertion error
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300459```
460<!--- TEST-->
461
Roman Elizarov3258e1f2019-08-22 20:08:48 +0300462#### Exceptions in supervised coroutines
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300463
464Another crucial difference between regular and supervisor jobs is exception handling.
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700465Every child should handle its exceptions by itself via the exception handling mechanism.
466This difference comes from the fact that child's failure does not propagate to the parent.
Roman Elizaroveb1b5db2020-10-16 10:45:08 +0300467It means that coroutines launched directly inside the [supervisorScope][_supervisorScope] _do_ use the [CoroutineExceptionHandler]
Roman Elizarov94970df2020-04-22 13:58:16 +0300468that is installed in their scope in the same way as root coroutines do
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700469(see the [CoroutineExceptionHandler](#coroutineexceptionhandler) section for details).
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300470
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300471<div class="sample" markdown="1" theme="idea" data-highlight-only>
472
473```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300474import kotlin.coroutines.*
475import kotlinx.coroutines.*
476
477fun main() = runBlocking {
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300478 val handler = CoroutineExceptionHandler { _, exception ->
Roman Elizarov94970df2020-04-22 13:58:16 +0300479 println("CoroutineExceptionHandler got $exception")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300480 }
481 supervisorScope {
482 val child = launch(handler) {
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700483 println("The child throws an exception")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300484 throw AssertionError()
485 }
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700486 println("The scope is completing")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300487 }
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700488 println("The scope is completed")
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300489}
490```
491
492</div>
493
Adam Howardf13549a2020-06-02 11:17:46 +0100494> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-supervision-03.kt).
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300495
496The output of this code is:
497
498```text
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700499The scope is completing
500The child throws an exception
Roman Elizarov94970df2020-04-22 13:58:16 +0300501CoroutineExceptionHandler got java.lang.AssertionError
Yanis Baturaf4fb2042020-06-21 20:03:40 +0700502The scope is completed
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300503```
504<!--- TEST-->
505
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300506<!--- MODULE kotlinx-coroutines-core -->
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300507<!--- INDEX kotlinx.coroutines -->
508[CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html
509[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html
510[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html
511[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html
512[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html
513[CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html
Roman Elizarov94970df2020-04-22 13:58:16 +0300514[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
515[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300516[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html
517[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
518[SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-supervisor-job.html
519[Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job.html
Roman Elizaroveb1b5db2020-10-16 10:45:08 +0300520[_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
521[_supervisorScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/supervisor-scope.html
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300522<!--- INDEX kotlinx.coroutines.channels -->
523[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html
524[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html
525[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300526<!--- END -->