blob: 8888c5657fc416d2e7f312c5d8eecc3a7c4de958 [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
88Exception in thread "ForkJoinPool.commonPool-worker-2 @coroutine#2" java.lang.IndexOutOfBoundsException
89Joined 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 ->
271 println("Caught $exception with suppressed ${exception.suppressed().contentToString()}")
272 }
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
295The output of this code is:
296
297```text
298Caught java.io.IOException with suppressed [java.lang.ArithmeticException]
299```
300
301<!--- TEST-->
302
303> Note, this mechanism currently works only on Java version 1.7+.
304Limitation on JS and Native is temporary and will be fixed in the future.
305
306Cancellation exceptions are transparent and unwrapped by default:
307
308<!--- INCLUDE
309import kotlin.coroutines.experimental.*
310import java.io.*
311-->
312
Alexander Prendotacbeef102018-09-27 18:42:04 +0300313<div class="sample" markdown="1" theme="idea" data-highlight-only>
314
hadihariri7db55532018-09-15 10:35:08 +0200315```kotlin
316fun main(args: Array<String>) = runBlocking {
317 val handler = CoroutineExceptionHandler { _, exception ->
318 println("Caught original $exception")
319 }
320 val job = GlobalScope.launch(handler) {
321 val inner = launch {
322 launch {
323 launch {
324 throw IOException()
325 }
326 }
327 }
328 try {
329 inner.join()
Vsevolod Tolstopyatova2d80882018-09-24 19:51:49 +0300330 } catch (e: CancellationException) {
331 println("Rethrowing CancellationException with original cause")
hadihariri7db55532018-09-15 10:35:08 +0200332 throw e
333 }
334 }
335 job.join()
336}
337```
338
Alexander Prendotacbeef102018-09-27 18:42:04 +0300339</div>
340
hadihariri7db55532018-09-15 10:35:08 +0200341> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-06.kt)
342
343The output of this code is:
344
345```text
Vsevolod Tolstopyatova2d80882018-09-24 19:51:49 +0300346Rethrowing CancellationException with original cause
hadihariri7db55532018-09-15 10:35:08 +0200347Caught original java.io.IOException
348```
349<!--- TEST-->
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300350
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300351## Supervision
352
353As we have studied before, cancellation is a bidirectional relationship propagating through the whole
354coroutines hierarchy. But what if unidirectional cancellation is required?
355
356Good example of such requirement can be a UI component with the job defined in its scope. If any of UI's child task
357has failed, it is not always necessary to cancel (effectively kill) the whole UI component,
358but 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.
359
360Another example is a server process that spawns several children jobs and needs to _supervise_
361their execution, tracking their failures and restarting just those children jobs that had failed.
362
363### Supervision job
364
365For these purposes [SupervisorJob][SupervisorJob()] can be used. It is similar to a regular [Job][Job()] with the only exception that cancellation is propagated
366only downwards. It is easy to demonstrate with an example:
367
368<!--- INCLUDE
369import kotlin.coroutines.experimental.*
370-->
371
372<div class="sample" markdown="1" theme="idea" data-highlight-only>
373
374```kotlin
375fun main(args: Array<String>) = runBlocking {
376 val supervisor = SupervisorJob()
377 with(CoroutineScope(coroutineContext + supervisor)) {
378 // launch the first child -- its exception is ignored for this example (don't do this in practise!)
379 val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) {
380 println("First child is failing")
381 throw AssertionError("First child is cancelled")
382 }
383 // launch the second child
384 val secondChild = launch {
385 firstChild.join()
386 // Cancellation of the first child is not propagated to the second child
387 println("First child is cancelled: ${firstChild.isCancelled}, but second one is still active")
388 try {
389 delay(Long.MAX_VALUE)
390 } finally {
391 // But cancellation of the supervisor is propagated
392 println("Second child is cancelled because supervisor is cancelled")
393 }
394 }
395 // wait until the first child fails & completes
396 firstChild.join()
397 println("Cancelling supervisor")
398 supervisor.cancel()
399 secondChild.join()
400 }
401}
402```
403
404</div>
405
406> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-supervision-01.kt)
407
408The output of this code is:
409
410```text
411First child is failing
412First child is cancelled: true, but second one is still active
413Cancelling supervisor
414Second child is cancelled because supervisor is cancelled
415```
416<!--- TEST-->
417
418
419### Supervision scope
420
421For *scoped* concurrency [supervisorScope] can be used instead of [coroutineScope] for the same purpose. It propagates cancellation
422only in one direction and cancels all children only if it has failed itself. It also waits for all children before completion
423just like [coroutineScope] does.
424
425<!--- INCLUDE
426import kotlin.coroutines.experimental.*
427-->
428
429<div class="sample" markdown="1" theme="idea" data-highlight-only>
430
431```kotlin
432fun main(args: Array<String>) = runBlocking {
433 try {
434 supervisorScope {
435 val child = launch {
436 try {
437 println("Child is sleeping")
438 delay(Long.MAX_VALUE)
439 } finally {
440 println("Child is cancelled")
441 }
442 }
443 // Give our child a chance to execute and print using yield
444 yield()
445 println("Throwing exception from scope")
446 throw AssertionError()
447 }
448 } catch(e: AssertionError) {
449 println("Caught assertion error")
450 }
451}
452```
453
454</div>
455
456> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-supervision-02.kt)
457
458The output of this code is:
459
460```text
461Child is sleeping
462Throwing exception from scope
463Child is cancelled
464Caught assertion error
465```
466<!--- TEST-->
467
468### Exceptions in supervised coroutines
469
470Another crucial difference between regular and supervisor jobs is exception handling.
471Every child should handle its exceptions by itself via exception handling mechanisms.
472This difference comes from the fact that child's failure is not propagated to the parent.
473
474<!--- INCLUDE
475import kotlin.coroutines.experimental.*
476-->
477
478<div class="sample" markdown="1" theme="idea" data-highlight-only>
479
480```kotlin
481fun main(args: Array<String>) = runBlocking {
482 val handler = CoroutineExceptionHandler { _, exception ->
483 println("Caught $exception")
484 }
485 supervisorScope {
486 val child = launch(handler) {
487 println("Child throws an exception")
488 throw AssertionError()
489 }
490 println("Scope is completing")
491 }
492 println("Scope is completed")
493}
494```
495
496</div>
497
498> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-supervision-03.kt)
499
500The output of this code is:
501
502```text
503Scope is completing
504Child throws an exception
505Caught java.lang.AssertionError
506Scope is completed
507```
508<!--- TEST-->
509
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300510<!--- MODULE kotlinx-coroutines-core -->
511<!--- INDEX kotlinx.coroutines.experimental -->
512[CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-cancellation-exception/index.html
513[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/await.html
514[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-global-scope/index.html
515[CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-exception-handler/index.html
516[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html
517[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/cancel.html
518[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/run-blocking.html
Vsevolod Tolstopyatov49f25a52018-09-28 13:34:10 +0300519[SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-supervisor-job.html
520[Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job.html
521[supervisorScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/supervisor-scope.html
522[coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/coroutine-scope.html
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300523<!--- INDEX kotlinx.coroutines.experimental.channels -->
524[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/actor.html
525[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/produce.html
526[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-receive-channel/receive.html
527<!--- END -->