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