• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!--- INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
2/*
3 * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
4 */
5
6// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
7package kotlinx.coroutines.guide.$$1$$2
8-->
9<!--- KNIT     ../kotlinx-coroutines-core/jvm/test/guide/.*\.kt -->
10<!--- TEST_OUT ../kotlinx-coroutines-core/jvm/test/guide/test/ExceptionsGuideTest.kt
11// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
12package kotlinx.coroutines.guide.test
13
14import org.junit.Test
15
16class ExceptionsGuideTest {
17-->
18**Table of contents**
19
20<!--- TOC -->
21
22* [Exception Handling](#exception-handling)
23  * [Exception propagation](#exception-propagation)
24  * [CoroutineExceptionHandler](#coroutineexceptionhandler)
25  * [Cancellation and exceptions](#cancellation-and-exceptions)
26  * [Exceptions aggregation](#exceptions-aggregation)
27  * [Supervision](#supervision)
28    * [Supervision job](#supervision-job)
29    * [Supervision scope](#supervision-scope)
30    * [Exceptions in supervised coroutines](#exceptions-in-supervised-coroutines)
31
32<!--- END_TOC -->
33
34## Exception Handling
35
36
37This section covers exception handling and cancellation on exceptions.
38We already know that cancelled coroutine throws [CancellationException] in suspension points and that it
39is ignored by coroutines machinery. But what happens if an exception is thrown during cancellation or multiple children of the same
40coroutine throw an exception?
41
42### Exception propagation
43
44Coroutine builders come in two flavors: propagating exceptions automatically ([launch] and [actor]) or
45exposing them to users ([async] and [produce]).
46The former treat exceptions as unhandled, similar to Java's `Thread.uncaughtExceptionHandler`,
47while the latter are relying on the user to consume the final
48exception, for example via [await][Deferred.await] or [receive][ReceiveChannel.receive]
49([produce] and [receive][ReceiveChannel.receive] are covered later in [Channels](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/channels.md) section).
50
51It can be demonstrated by a simple example that creates coroutines in the [GlobalScope]:
52
53<div class="sample" markdown="1" theme="idea" data-highlight-only>
54
55```kotlin
56import kotlinx.coroutines.*
57
58fun main() = runBlocking {
59    val job = GlobalScope.launch {
60        println("Throwing exception from launch")
61        throw IndexOutOfBoundsException() // Will be printed to the console by Thread.defaultUncaughtExceptionHandler
62    }
63    job.join()
64    println("Joined failed job")
65    val deferred = GlobalScope.async {
66        println("Throwing exception from async")
67        throw ArithmeticException() // Nothing is printed, relying on user to call await
68    }
69    try {
70        deferred.await()
71        println("Unreached")
72    } catch (e: ArithmeticException) {
73        println("Caught ArithmeticException")
74    }
75}
76```
77
78</div>
79
80> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt).
81
82The 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)):
83
84```text
85Throwing exception from launch
86Exception in thread "DefaultDispatcher-worker-2 @coroutine#2" java.lang.IndexOutOfBoundsException
87Joined failed job
88Throwing exception from async
89Caught ArithmeticException
90```
91
92<!--- TEST EXCEPTION-->
93
94### CoroutineExceptionHandler
95
96But what if one does not want to print all exceptions to the console?
97[CoroutineExceptionHandler] context element is used as generic `catch` block of coroutine where custom logging or exception handling may take place.
98It is similar to using [`Thread.uncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)).
99
100On JVM it is possible to redefine global exception handler for all coroutines by registering [CoroutineExceptionHandler] via
101[`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html).
102Global exception handler is similar to
103[`Thread.defaultUncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler))
104which is used when no more specific handlers are registered.
105On Android, `uncaughtExceptionPreHandler` is installed as a global coroutine exception handler.
106
107[CoroutineExceptionHandler] is invoked only on exceptions which are not expected to be handled by the user,
108so registering it in [async] builder and the like of it has no effect.
109
110<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
111
112```kotlin
113import kotlinx.coroutines.*
114
115fun main() = runBlocking {
116//sampleStart
117    val handler = CoroutineExceptionHandler { _, exception ->
118        println("Caught $exception")
119    }
120    val job = GlobalScope.launch(handler) {
121        throw AssertionError()
122    }
123    val deferred = GlobalScope.async(handler) {
124        throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await()
125    }
126    joinAll(job, deferred)
127//sampleEnd
128}
129```
130
131</div>
132
133> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt).
134
135The output of this code is:
136
137```text
138Caught java.lang.AssertionError
139```
140
141<!--- TEST-->
142
143### Cancellation and exceptions
144
145Cancellation is tightly bound with exceptions. Coroutines internally use `CancellationException` for cancellation, these
146exceptions are ignored by all handlers, so they should be used only as the source of additional debug information, which can
147be obtained by `catch` block.
148When a coroutine is cancelled using [Job.cancel] without a cause, it terminates, but it does not cancel its parent.
149Cancelling without cause is a mechanism for parent to cancel its children without cancelling itself.
150
151<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
152
153```kotlin
154import kotlinx.coroutines.*
155
156fun main() = runBlocking {
157//sampleStart
158    val job = launch {
159        val child = launch {
160            try {
161                delay(Long.MAX_VALUE)
162            } finally {
163                println("Child is cancelled")
164            }
165        }
166        yield()
167        println("Cancelling child")
168        child.cancel()
169        child.join()
170        yield()
171        println("Parent is not cancelled")
172    }
173    job.join()
174//sampleEnd
175}
176```
177
178</div>
179
180> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt).
181
182The output of this code is:
183
184```text
185Cancelling child
186Child is cancelled
187Parent is not cancelled
188```
189
190<!--- TEST-->
191
192If a coroutine encounters exception other than `CancellationException`, it cancels its parent with that exception.
193This behaviour cannot be overridden and is used to provide stable coroutines hierarchies for
194[structured concurrency](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/composing-suspending-functions.md#structured-concurrency-with-async) which do not depend on
195[CoroutineExceptionHandler] implementation.
196The original exception is handled by the parent when all its children terminate.
197
198> This also a reason why, in these examples, [CoroutineExceptionHandler] is always installed to a coroutine
199that is created in [GlobalScope]. It does not make sense to install an exception handler to a coroutine that
200is launched in the scope of the main [runBlocking], since the main coroutine is going to be always cancelled
201when its child completes with exception despite the installed handler.
202
203<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
204
205```kotlin
206import kotlinx.coroutines.*
207
208fun main() = runBlocking {
209//sampleStart
210    val handler = CoroutineExceptionHandler { _, exception ->
211        println("Caught $exception")
212    }
213    val job = GlobalScope.launch(handler) {
214        launch { // the first child
215            try {
216                delay(Long.MAX_VALUE)
217            } finally {
218                withContext(NonCancellable) {
219                    println("Children are cancelled, but exception is not handled until all children terminate")
220                    delay(100)
221                    println("The first child finished its non cancellable block")
222                }
223            }
224        }
225        launch { // the second child
226            delay(10)
227            println("Second child throws an exception")
228            throw ArithmeticException()
229        }
230    }
231    job.join()
232//sampleEnd
233}
234```
235
236</div>
237
238> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt).
239
240The output of this code is:
241
242```text
243Second child throws an exception
244Children are cancelled, but exception is not handled until all children terminate
245The first child finished its non cancellable block
246Caught java.lang.ArithmeticException
247```
248<!--- TEST-->
249
250### Exceptions aggregation
251
252What happens if multiple children of a coroutine throw an exception?
253The general rule is "the first exception wins", so the first thrown exception is exposed to the handler.
254But that may cause lost exceptions, for example if coroutine throws an exception in its `finally` block.
255So, additional exceptions are suppressed.
256
257> One of the solutions would have been to report each exception separately,
258but then [Deferred.await] should have had the same mechanism to avoid behavioural inconsistency and this
259would cause implementation details of a coroutines (whether it had delegated parts of its work to its children or not)
260to leak to its exception handler.
261
262
263<!--- INCLUDE
264
265import kotlinx.coroutines.exceptions.*
266-->
267
268<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
269
270```kotlin
271import kotlinx.coroutines.*
272import java.io.*
273
274fun main() = runBlocking {
275    val handler = CoroutineExceptionHandler { _, exception ->
276        println("Caught $exception with suppressed ${exception.suppressed.contentToString()}")
277    }
278    val job = GlobalScope.launch(handler) {
279        launch {
280            try {
281                delay(Long.MAX_VALUE)
282            } finally {
283                throw ArithmeticException()
284            }
285        }
286        launch {
287            delay(100)
288            throw IOException()
289        }
290        delay(Long.MAX_VALUE)
291    }
292    job.join()
293}
294```
295
296</div>
297
298> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt).
299
300> Note: This above code will work properly only on JDK7+ that supports `suppressed` exceptions
301
302The output of this code is:
303
304```text
305Caught java.io.IOException with suppressed [java.lang.ArithmeticException]
306```
307
308<!--- TEST-->
309
310> Note, this mechanism currently works only on Java version 1.7+.
311Limitation on JS and Native is temporary and will be fixed in the future.
312
313Cancellation exceptions are transparent and unwrapped by default:
314
315<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
316
317```kotlin
318import kotlinx.coroutines.*
319import java.io.*
320
321fun main() = runBlocking {
322//sampleStart
323    val handler = CoroutineExceptionHandler { _, exception ->
324        println("Caught original $exception")
325    }
326    val job = GlobalScope.launch(handler) {
327        val inner = launch {
328            launch {
329                launch {
330                    throw IOException()
331                }
332            }
333        }
334        try {
335            inner.join()
336        } catch (e: CancellationException) {
337            println("Rethrowing CancellationException with original cause")
338            throw e
339        }
340    }
341    job.join()
342//sampleEnd
343}
344```
345
346</div>
347
348> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt).
349
350The output of this code is:
351
352```text
353Rethrowing CancellationException with original cause
354Caught original java.io.IOException
355```
356<!--- TEST-->
357
358### Supervision
359
360As we have studied before, cancellation is a bidirectional relationship propagating through the whole
361coroutines hierarchy. But what if unidirectional cancellation is required?
362
363A 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
364have failed, it is not always necessary to cancel (effectively kill) the whole UI component,
365but 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 required.
366
367Another example is a server process that spawns several children jobs and needs to _supervise_
368their execution, tracking their failures and restarting just those children jobs that had failed.
369
370#### Supervision job
371
372For these purposes [SupervisorJob][SupervisorJob()] can be used. It is similar to a regular [Job][Job()] with the only exception that cancellation is propagated
373only downwards. It is easy to demonstrate with an example:
374
375<div class="sample" markdown="1" theme="idea" data-highlight-only>
376
377```kotlin
378import kotlinx.coroutines.*
379
380fun main() = runBlocking {
381    val supervisor = SupervisorJob()
382    with(CoroutineScope(coroutineContext + supervisor)) {
383        // launch the first child -- its exception is ignored for this example (don't do this in practice!)
384        val firstChild = launch(CoroutineExceptionHandler { _, _ ->  }) {
385            println("First child is failing")
386            throw AssertionError("First child is cancelled")
387        }
388        // launch the second child
389        val secondChild = launch {
390            firstChild.join()
391            // Cancellation of the first child is not propagated to the second child
392            println("First child is cancelled: ${firstChild.isCancelled}, but second one is still active")
393            try {
394                delay(Long.MAX_VALUE)
395            } finally {
396                // But cancellation of the supervisor is propagated
397                println("Second child is cancelled because supervisor is cancelled")
398            }
399        }
400        // wait until the first child fails & completes
401        firstChild.join()
402        println("Cancelling supervisor")
403        supervisor.cancel()
404        secondChild.join()
405    }
406}
407```
408
409</div>
410
411> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-supervision-01.kt).
412
413The output of this code is:
414
415```text
416First child is failing
417First child is cancelled: true, but second one is still active
418Cancelling supervisor
419Second child is cancelled because supervisor is cancelled
420```
421<!--- TEST-->
422
423
424#### Supervision scope
425
426For *scoped* concurrency [supervisorScope] can be used instead of [coroutineScope] for the same purpose. It propagates cancellation
427only in one direction and cancels all children only if it has failed itself. It also waits for all children before completion
428just like [coroutineScope] does.
429
430<div class="sample" markdown="1" theme="idea" data-highlight-only>
431
432```kotlin
433import kotlin.coroutines.*
434import kotlinx.coroutines.*
435
436fun main() = runBlocking {
437    try {
438        supervisorScope {
439            val child = launch {
440                try {
441                    println("Child is sleeping")
442                    delay(Long.MAX_VALUE)
443                } finally {
444                    println("Child is cancelled")
445                }
446            }
447            // Give our child a chance to execute and print using yield
448            yield()
449            println("Throwing exception from scope")
450            throw AssertionError()
451        }
452    } catch(e: AssertionError) {
453        println("Caught assertion error")
454    }
455}
456```
457
458</div>
459
460> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-supervision-02.kt).
461
462The output of this code is:
463
464```text
465Child is sleeping
466Throwing exception from scope
467Child is cancelled
468Caught assertion error
469```
470<!--- TEST-->
471
472#### Exceptions in supervised coroutines
473
474Another crucial difference between regular and supervisor jobs is exception handling.
475Every child should handle its exceptions by itself via exception handling mechanisms.
476This difference comes from the fact that child's failure is not propagated to the parent.
477
478<div class="sample" markdown="1" theme="idea" data-highlight-only>
479
480```kotlin
481import kotlin.coroutines.*
482import kotlinx.coroutines.*
483
484fun main() = runBlocking {
485    val handler = CoroutineExceptionHandler { _, exception ->
486        println("Caught $exception")
487    }
488    supervisorScope {
489        val child = launch(handler) {
490            println("Child throws an exception")
491            throw AssertionError()
492        }
493        println("Scope is completing")
494    }
495    println("Scope is completed")
496}
497```
498
499</div>
500
501> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-supervision-03.kt).
502
503The output of this code is:
504
505```text
506Scope is completing
507Child throws an exception
508Caught java.lang.AssertionError
509Scope is completed
510```
511<!--- TEST-->
512
513<!--- MODULE kotlinx-coroutines-core -->
514<!--- INDEX kotlinx.coroutines -->
515[CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html
516[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html
517[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html
518[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html
519[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html
520[CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html
521[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html
522[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html
523[SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-supervisor-job.html
524[Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job.html
525[supervisorScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/supervisor-scope.html
526[coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
527<!--- INDEX kotlinx.coroutines.channels -->
528[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html
529[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html
530[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
531<!--- END -->
532