You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Further clarifications and better style for exception handling
* Consistent terminology on "uncaught exceptions".
* Clarified special relations of exception handling with supervision.
* Clearer text in CoroutineExceptionHandler examples.
* Minor stylistic corrections.
Fixes#871
Copy file name to clipboardExpand all lines: docs/exception-handling.md
+51-49Lines changed: 51 additions & 49 deletions
Original file line number
Diff line number
Diff line change
@@ -18,36 +18,36 @@
18
18
19
19
## Exception Handling
20
20
21
-
22
21
This section covers exception handling and cancellation on exceptions.
23
-
We already know that cancelled coroutine throws [CancellationException] in suspension points and that it
24
-
is ignored by coroutines machinery. But what happens if an exception is thrown during cancellation or multiple children of the same
25
-
coroutine throw an exception?
22
+
We already know that cancelled coroutine throws [CancellationException] in suspension points and that it
23
+
is ignored by the coroutines' machinery. Here we look at what happens if an exception is thrown during cancellation or multiple children of the same
24
+
coroutine throw an exception.
26
25
27
26
### Exception propagation
28
27
29
-
Coroutine builders come in two flavors: propagating exceptions automatically ([launch] and [actor]) or
28
+
Coroutine builders come in two flavors: propagating exceptions automatically ([launch] and [actor]) or
30
29
exposing them to users ([async] and [produce]).
31
-
The former treat exceptions as unhandled, similar to Java's `Thread.uncaughtExceptionHandler`,
32
-
while the latter are relying on the user to consume the final
30
+
When these builders are used to create a _root_ coroutine, that is not a _child_ of another coroutine,
31
+
the former builder treat exceptions as **uncaught** exceptions, similar to Java's `Thread.uncaughtExceptionHandler`,
32
+
while the latter are relying on the user to consume the final
33
33
exception, for example via [await][Deferred.await] or [receive][ReceiveChannel.receive]
34
34
([produce] and [receive][ReceiveChannel.receive] are covered later in [Channels](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/channels.md) section).
35
35
36
-
It can be demonstrated by a simple example that creates coroutines in the [GlobalScope]:
36
+
It can be demonstrated by a simple example that creates root coroutines using the [GlobalScope]:
val job =GlobalScope.launch {// root coroutine with launch
45
45
println("Throwing exception from launch")
46
46
throwIndexOutOfBoundsException() // Will be printed to the console by Thread.defaultUncaughtExceptionHandler
47
47
}
48
48
job.join()
49
49
println("Joined failed job")
50
-
val deferred =GlobalScope.async {
50
+
val deferred =GlobalScope.async {// root coroutine with async
51
51
println("Throwing exception from async")
52
52
throwArithmeticException() // Nothing is printed, relying on user to call await
53
53
}
@@ -78,7 +78,7 @@ Caught ArithmeticException
78
78
79
79
### CoroutineExceptionHandler
80
80
81
-
What if one does not want to print all exceptions to the console?
81
+
It is possible to customize the default behavior of printing **uncaught**exceptions to the console.
82
82
[CoroutineExceptionHandler] context element on a _root_ coroutine can be used as generic `catch` block for
83
83
this root coroutine and all its children where custom exception handling may take place.
84
84
It is similar to [`Thread.uncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)).
@@ -100,6 +100,9 @@ so the `CoroutineExceptionHandler` installed in their context is never used.
100
100
In addition to that, [async] builder always catches all exceptions and represents them in the resulting [Deferred] object,
101
101
so its `CoroutineExceptionHandler` has no effect either.
102
102
103
+
> Coroutines running in supervision scope do not propagate exceptions to their parent and are
104
+
excluded from this rule. A further [Supervision](#supervision) section of this document gives more details.
Cancellation is tightly bound with exceptions. Coroutines internally use `CancellationException` for cancellation, these
141
+
Cancellation is closely related to exceptions. Coroutines internally use `CancellationException` for cancellation, these
139
142
exceptions are ignored by all handlers, so they should be used only as the source of additional debug information, which can
140
143
be obtained by `catch` block.
141
144
When a coroutine is cancelled using [Job.cancel], it terminates, but it does not cancel its parent.
@@ -183,15 +186,17 @@ Parent is not cancelled
183
186
184
187
If a coroutine encounters an exception other than `CancellationException`, it cancels its parent with that exception.
185
188
This behaviour cannot be overridden and is used to provide stable coroutines hierarchies for
186
-
[structured concurrency](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/composing-suspending-functions.md#structured-concurrency-with-async) which do not depend on
187
-
[CoroutineExceptionHandler] implementation.
188
-
The original exception is handled by the parent when all its children terminate.
val inner = launch {// all this stack of coroutines will get cancelled
319
317
launch {
320
318
launch {
321
-
throwIOException()
319
+
throwIOException()// the original exception
322
320
}
323
321
}
324
322
}
325
323
try {
326
324
inner.join()
327
325
} catch (e:CancellationException) {
328
326
println("Rethrowing CancellationException with original cause")
329
-
throw e
327
+
throw e// cancellation exception is rethrown, yet the original IOException gets to the handler
330
328
}
331
329
}
332
330
job.join()
@@ -342,25 +340,26 @@ The output of this code is:
342
340
343
341
```text
344
342
Rethrowing CancellationException with original cause
345
-
Caught original java.io.IOException
343
+
CoroutineExceptionHandler got java.io.IOException
346
344
```
347
345
<!--- TEST-->
348
346
349
347
### Supervision
350
348
351
349
As we have studied before, cancellation is a bidirectional relationship propagating through the whole
352
-
coroutines hierarchy. But what if unidirectional cancellation is required?
350
+
hierarchy of coroutines. Let us take a look at the case when unidirectional cancellation is required.
353
351
354
352
A 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
355
353
have failed, it is not always necessary to cancel (effectively kill) the whole UI component,
356
-
but 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.
354
+
but 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 needed.
357
355
358
356
Another example is a server process that spawns several children jobs and needs to _supervise_
359
357
their execution, tracking their failures and restarting just those children jobs that had failed.
360
358
361
359
#### Supervision job
362
360
363
-
For these purposes [SupervisorJob][SupervisorJob()] can be used. It is similar to a regular [Job][Job()] with the only exception that cancellation is propagated
361
+
For these purposes [SupervisorJob][SupervisorJob()] can be used.
362
+
It is similar to a regular [Job][Job()] with the only exception that cancellation is propagated
364
363
only downwards. It is easy to demonstrate with an example:
0 commit comments