blob: 7342fc734ef6be0c824b6adc742ce08a2b886ea9 [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]
49([produce] and [receive][ReceiveChannel.receive] are covered later in [Channels](#channels) section).
50
51It can be demonstrated by a simple example that creates new coroutines in [GlobalScope]:
52
53```kotlin
54fun main(args: Array<String>) = runBlocking {
55 val job = GlobalScope.launch {
56 println("Throwing exception from launch")
57 throw IndexOutOfBoundsException() // Will be printed to the console by Thread.defaultUncaughtExceptionHandler
58 }
59 job.join()
60 println("Joined failed job")
61 val deferred = GlobalScope.async {
62 println("Throwing exception from async")
63 throw ArithmeticException() // Nothing is printed, relying on user to call await
64 }
65 try {
66 deferred.await()
67 println("Unreached")
68 } catch (e: ArithmeticException) {
69 println("Caught ArithmeticException")
70 }
71}
72```
73
74> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-01.kt)
75
76The output of this code is (with [debug](#debugging-coroutines-and-threads)):
77
78```text
79Throwing exception from launch
80Exception in thread "ForkJoinPool.commonPool-worker-2 @coroutine#2" java.lang.IndexOutOfBoundsException
81Joined failed job
82Throwing exception from async
83Caught ArithmeticException
84```
85
86<!--- TEST EXCEPTION-->
87
88### CoroutineExceptionHandler
89
90But what if one does not want to print all exceptions to the console?
91[CoroutineExceptionHandler] context element is used as generic `catch` block of coroutine where custom logging or exception handling may take place.
92It is similar to using [`Thread.uncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)).
93
94On JVM it is possible to redefine global exception handler for all coroutines by registering [CoroutineExceptionHandler] via
95[`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html).
96Global exception handler is similar to
97[`Thread.defaultUncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler))
98which is used when no more specific handlers are registered.
99On Android, `uncaughtExceptionPreHandler` is installed as a global coroutine exception handler.
100
101[CoroutineExceptionHandler] is invoked only on exceptions which are not expected to be handled by the user,
102so registering it in [async] builder and the like of it has no effect.
103
104```kotlin
105fun main(args: Array<String>) = runBlocking {
106 val handler = CoroutineExceptionHandler { _, exception ->
107 println("Caught $exception")
108 }
109 val job = GlobalScope.launch(handler) {
110 throw AssertionError()
111 }
112 val deferred = GlobalScope.async(handler) {
113 throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await()
114 }
115 joinAll(job, deferred)
116}
117```
118
119> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-02.kt)
120
121The output of this code is:
122
123```text
124Caught java.lang.AssertionError
125```
126
127<!--- TEST-->
128
129### Cancellation and exceptions
130
131Cancellation is tightly bound with exceptions. Coroutines internally use `CancellationException` for cancellation, these
132exceptions are ignored by all handlers, so they should be used only as the source of additional debug information, which can
133be obtained by `catch` block.
134When a coroutine is cancelled using [Job.cancel] without a cause, it terminates, but it does not cancel its parent.
135Cancelling without cause is a mechanism for parent to cancel its children without cancelling itself.
136
137<!--- INCLUDE
138import kotlin.coroutines.experimental.*
139-->
140
141```kotlin
142fun main(args: Array<String>) = runBlocking {
143 val job = launch {
144 val child = launch {
145 try {
146 delay(Long.MAX_VALUE)
147 } finally {
148 println("Child is cancelled")
149 }
150 }
151 yield()
152 println("Cancelling child")
153 child.cancel()
154 child.join()
155 yield()
156 println("Parent is not cancelled")
157 }
158 job.join()
159}
160```
161
162> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-03.kt)
163
164The output of this code is:
165
166```text
167Cancelling child
168Child is cancelled
169Parent is not cancelled
170```
171
172<!--- TEST-->
173
174If a coroutine encounters exception other than `CancellationException`, it cancels its parent with that exception.
175This behaviour cannot be overridden and is used to provide stable coroutines hierarchies for
176[structured concurrency](#structured-concurrency) which do not depend on
177[CoroutineExceptionHandler] implementation.
178The original exception is handled by the parent when all its children terminate.
179
180> This also a reason why, in these examples, [CoroutineExceptionHandler] is always installed to a coroutine
181that is created in [GlobalScope]. It does not make sense to install an exception handler to a coroutine that
182is launched in the scope of the main [runBlocking], since the main coroutine is going to be always cancelled
183when its child completes with exception despite the installed handler.
184
185<!--- INCLUDE
186import kotlin.coroutines.experimental.*
187-->
188
189```kotlin
190fun main(args: Array<String>) = runBlocking {
191 val handler = CoroutineExceptionHandler { _, exception ->
192 println("Caught $exception")
193 }
194 val job = GlobalScope.launch(handler) {
195 launch { // the first child
196 try {
197 delay(Long.MAX_VALUE)
198 } finally {
199 withContext(NonCancellable) {
200 println("Children are cancelled, but exception is not handled until all children terminate")
201 delay(100)
202 println("The first child finished its non cancellable block")
203 }
204 }
205 }
206 launch { // the second child
207 delay(10)
208 println("Second child throws an exception")
209 throw ArithmeticException()
210 }
211 }
212 job.join()
213}
214```
215
216> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-04.kt)
217
218The output of this code is:
219
220```text
221Second child throws an exception
222Children are cancelled, but exception is not handled until all children terminate
223The first child finished its non cancellable block
224Caught java.lang.ArithmeticException
225```
226<!--- TEST-->
227
228### Exceptions aggregation
229
230What happens if multiple children of a coroutine throw an exception?
231The general rule is "the first exception wins", so the first thrown exception is exposed to the handler.
232But that may cause lost exceptions, for example if coroutine throws an exception in its `finally` block.
233So, additional exceptions are suppressed.
234
235> One of the solutions would have been to report each exception separately,
236but then [Deferred.await] should have had the same mechanism to avoid behavioural inconsistency and this
237would cause implementation details of a coroutines (whether it had delegate parts of its work to its children or not)
238to leak to its exception handler.
239
240<!--- INCLUDE
241import kotlinx.coroutines.experimental.exceptions.*
242import kotlin.coroutines.experimental.*
243import java.io.*
244-->
245
246```kotlin
247fun main(args: Array<String>) = runBlocking {
248 val handler = CoroutineExceptionHandler { _, exception ->
249 println("Caught $exception with suppressed ${exception.suppressed().contentToString()}")
250 }
251 val job = GlobalScope.launch(handler) {
252 launch {
253 try {
254 delay(Long.MAX_VALUE)
255 } finally {
256 throw ArithmeticException()
257 }
258 }
259 launch {
260 throw IOException()
261 }
262 delay(Long.MAX_VALUE)
263 }
264 job.join()
265}
266```
267
268> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-05.kt)
269
270The output of this code is:
271
272```text
273Caught java.io.IOException with suppressed [java.lang.ArithmeticException]
274```
275
276<!--- TEST-->
277
278> Note, this mechanism currently works only on Java version 1.7+.
279Limitation on JS and Native is temporary and will be fixed in the future.
280
281Cancellation exceptions are transparent and unwrapped by default:
282
283<!--- INCLUDE
284import kotlin.coroutines.experimental.*
285import java.io.*
286-->
287
288```kotlin
289fun main(args: Array<String>) = runBlocking {
290 val handler = CoroutineExceptionHandler { _, exception ->
291 println("Caught original $exception")
292 }
293 val job = GlobalScope.launch(handler) {
294 val inner = launch {
295 launch {
296 launch {
297 throw IOException()
298 }
299 }
300 }
301 try {
302 inner.join()
303 } catch (e: JobCancellationException) {
304 println("Rethrowing JobCancellationException with original cause")
305 throw e
306 }
307 }
308 job.join()
309}
310```
311
312> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-exceptions-06.kt)
313
314The output of this code is:
315
316```text
317Rethrowing JobCancellationException with original cause
318Caught original java.io.IOException
319```
320<!--- TEST-->