blob: 6f2f4d2b7a83bacab95d6a98954fe36a36d6de41 [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)
29
30<!--- END_TOC -->
31
32## Exception handling
33
34<!--- INCLUDE .*/example-exceptions-([0-9]+).kt
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.uncaughExceptionHandler`,
47while the latter are relying on the user to consume the final
48exception, for example via [await][Deferred.await] or [receive][ReceiveChannel.receive]
Alexander Prendotacbeef102018-09-27 18:42:04 +030049([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 +020050
51It can be demonstrated by a simple example that creates new coroutines in [GlobalScope]:
52
Alexander Prendotacbeef102018-09-27 18:42:04 +030053<div class="sample" markdown="1" theme="idea" data-highlight-only>
54
hadihariri7db55532018-09-15 10:35:08 +020055```kotlin
56fun main(args: Array<String>) = runBlocking {
57 val job = GlobalScope.launch {
58 println("Throwing exception from launch")
59 throw IndexOutOfBoundsException() // Will be printed to the console by Thread.defaultUncaughtExceptionHandler
60 }
61 job.join()
62 println("Joined failed job")
63 val deferred = GlobalScope.async {
64 println("Throwing exception from async")
65 throw ArithmeticException() // Nothing is printed, relying on user to call await
66 }
67 try {
68 deferred.await()
69 println("Unreached")
70 } catch (e: ArithmeticException) {
71 println("Caught ArithmeticException")
72 }
73}
74```
75
Alexander Prendotacbeef102018-09-27 18:42:04 +030076</div>
77
hadihariri7db55532018-09-15 10:35:08 +020078> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-01.kt)
79
Alexander Prendotacbeef102018-09-27 18:42:04 +030080The 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 +020081
82```text
83Throwing exception from launch
84Exception in thread "ForkJoinPool.commonPool-worker-2 @coroutine#2" java.lang.IndexOutOfBoundsException
85Joined failed job
86Throwing exception from async
87Caught ArithmeticException
88```
89
90<!--- TEST EXCEPTION-->
91
92### CoroutineExceptionHandler
93
94But what if one does not want to print all exceptions to the console?
95[CoroutineExceptionHandler] context element is used as generic `catch` block of coroutine where custom logging or exception handling may take place.
96It is similar to using [`Thread.uncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)).
97
98On JVM it is possible to redefine global exception handler for all coroutines by registering [CoroutineExceptionHandler] via
99[`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html).
100Global exception handler is similar to
101[`Thread.defaultUncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler))
102which is used when no more specific handlers are registered.
103On Android, `uncaughtExceptionPreHandler` is installed as a global coroutine exception handler.
104
105[CoroutineExceptionHandler] is invoked only on exceptions which are not expected to be handled by the user,
106so registering it in [async] builder and the like of it has no effect.
107
Alexander Prendotacbeef102018-09-27 18:42:04 +0300108<div class="sample" markdown="1" theme="idea" data-highlight-only>
109
hadihariri7db55532018-09-15 10:35:08 +0200110```kotlin
111fun main(args: Array<String>) = runBlocking {
112 val handler = CoroutineExceptionHandler { _, exception ->
113 println("Caught $exception")
114 }
115 val job = GlobalScope.launch(handler) {
116 throw AssertionError()
117 }
118 val deferred = GlobalScope.async(handler) {
119 throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await()
120 }
121 joinAll(job, deferred)
122}
123```
124
Alexander Prendotacbeef102018-09-27 18:42:04 +0300125</div>
126
hadihariri7db55532018-09-15 10:35:08 +0200127> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-02.kt)
128
129The output of this code is:
130
131```text
132Caught java.lang.AssertionError
133```
134
135<!--- TEST-->
136
137### Cancellation and exceptions
138
139Cancellation is tightly bound with exceptions. Coroutines internally use `CancellationException` for cancellation, these
140exceptions are ignored by all handlers, so they should be used only as the source of additional debug information, which can
141be obtained by `catch` block.
142When a coroutine is cancelled using [Job.cancel] without a cause, it terminates, but it does not cancel its parent.
143Cancelling without cause is a mechanism for parent to cancel its children without cancelling itself.
144
145<!--- INCLUDE
146import kotlin.coroutines.experimental.*
147-->
148
Alexander Prendotacbeef102018-09-27 18:42:04 +0300149<div class="sample" markdown="1" theme="idea" data-highlight-only>
150
hadihariri7db55532018-09-15 10:35:08 +0200151```kotlin
152fun main(args: Array<String>) = runBlocking {
153 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()
169}
170```
171
Alexander Prendotacbeef102018-09-27 18:42:04 +0300172</div>
173
hadihariri7db55532018-09-15 10:35:08 +0200174> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-03.kt)
175
176The output of this code is:
177
178```text
179Cancelling child
180Child is cancelled
181Parent is not cancelled
182```
183
184<!--- TEST-->
185
186If a coroutine encounters exception other than `CancellationException`, it cancels its parent with that exception.
187This behaviour cannot be overridden and is used to provide stable coroutines hierarchies for
Alexander Prendotacbeef102018-09-27 18:42:04 +0300188[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 +0200189[CoroutineExceptionHandler] implementation.
190The original exception is handled by the parent when all its children terminate.
191
192> This also a reason why, in these examples, [CoroutineExceptionHandler] is always installed to a coroutine
193that 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
197<!--- INCLUDE
198import kotlin.coroutines.experimental.*
199-->
200
Alexander Prendotacbeef102018-09-27 18:42:04 +0300201<div class="sample" markdown="1" theme="idea" data-highlight-only>
202
hadihariri7db55532018-09-15 10:35:08 +0200203```kotlin
204fun main(args: Array<String>) = runBlocking {
205 val handler = CoroutineExceptionHandler { _, exception ->
206 println("Caught $exception")
207 }
208 val job = GlobalScope.launch(handler) {
209 launch { // the first child
210 try {
211 delay(Long.MAX_VALUE)
212 } finally {
213 withContext(NonCancellable) {
214 println("Children are cancelled, but exception is not handled until all children terminate")
215 delay(100)
216 println("The first child finished its non cancellable block")
217 }
218 }
219 }
220 launch { // the second child
221 delay(10)
222 println("Second child throws an exception")
223 throw ArithmeticException()
224 }
225 }
226 job.join()
227}
228```
229
Alexander Prendotacbeef102018-09-27 18:42:04 +0300230</div>
231
hadihariri7db55532018-09-15 10:35:08 +0200232> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-04.kt)
233
234The output of this code is:
235
236```text
237Second child throws an exception
238Children are cancelled, but exception is not handled until all children terminate
239The first child finished its non cancellable block
240Caught java.lang.ArithmeticException
241```
242<!--- TEST-->
243
244### Exceptions aggregation
245
246What happens if multiple children of a coroutine throw an exception?
247The general rule is "the first exception wins", so the first thrown exception is exposed to the handler.
248But that may cause lost exceptions, for example if coroutine throws an exception in its `finally` block.
249So, additional exceptions are suppressed.
250
251> One of the solutions would have been to report each exception separately,
252but then [Deferred.await] should have had the same mechanism to avoid behavioural inconsistency and this
253would cause implementation details of a coroutines (whether it had delegate parts of its work to its children or not)
254to leak to its exception handler.
255
256<!--- INCLUDE
257import kotlinx.coroutines.experimental.exceptions.*
258import kotlin.coroutines.experimental.*
259import java.io.*
260-->
261
Alexander Prendotacbeef102018-09-27 18:42:04 +0300262<div class="sample" markdown="1" theme="idea" data-highlight-only>
263
hadihariri7db55532018-09-15 10:35:08 +0200264```kotlin
265fun main(args: Array<String>) = runBlocking {
266 val handler = CoroutineExceptionHandler { _, exception ->
267 println("Caught $exception with suppressed ${exception.suppressed().contentToString()}")
268 }
269 val job = GlobalScope.launch(handler) {
270 launch {
271 try {
272 delay(Long.MAX_VALUE)
273 } finally {
274 throw ArithmeticException()
275 }
276 }
277 launch {
278 throw IOException()
279 }
280 delay(Long.MAX_VALUE)
281 }
282 job.join()
283}
284```
285
Alexander Prendotacbeef102018-09-27 18:42:04 +0300286</div>
287
hadihariri7db55532018-09-15 10:35:08 +0200288> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-05.kt)
289
290The output of this code is:
291
292```text
293Caught java.io.IOException with suppressed [java.lang.ArithmeticException]
294```
295
296<!--- TEST-->
297
298> Note, this mechanism currently works only on Java version 1.7+.
299Limitation on JS and Native is temporary and will be fixed in the future.
300
301Cancellation exceptions are transparent and unwrapped by default:
302
303<!--- INCLUDE
304import kotlin.coroutines.experimental.*
305import java.io.*
306-->
307
Alexander Prendotacbeef102018-09-27 18:42:04 +0300308<div class="sample" markdown="1" theme="idea" data-highlight-only>
309
hadihariri7db55532018-09-15 10:35:08 +0200310```kotlin
311fun main(args: Array<String>) = runBlocking {
312 val handler = CoroutineExceptionHandler { _, exception ->
313 println("Caught original $exception")
314 }
315 val job = GlobalScope.launch(handler) {
316 val inner = launch {
317 launch {
318 launch {
319 throw IOException()
320 }
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")
hadihariri7db55532018-09-15 10:35:08 +0200327 throw e
328 }
329 }
330 job.join()
331}
332```
333
Alexander Prendotacbeef102018-09-27 18:42:04 +0300334</div>
335
hadihariri7db55532018-09-15 10:35:08 +0200336> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-06.kt)
337
338The output of this code is:
339
340```text
Vsevolod Tolstopyatova2d80882018-09-24 19:51:49 +0300341Rethrowing CancellationException with original cause
hadihariri7db55532018-09-15 10:35:08 +0200342Caught original java.io.IOException
343```
344<!--- TEST-->
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300345
346<!--- MODULE kotlinx-coroutines-core -->
347<!--- INDEX kotlinx.coroutines.experimental -->
348[CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-cancellation-exception/index.html
349[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/await.html
350[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-global-scope/index.html
351[CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-exception-handler/index.html
352[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html
353[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/cancel.html
354[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/run-blocking.html
355<!--- INDEX kotlinx.coroutines.experimental.channels -->
356[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/actor.html
357[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/produce.html
358[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-receive-channel/receive.html
359<!--- END -->