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