Exception propagation
|
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = GlobalScope.launch { // root coroutine with launch
println("Throwing exception from launch")
throw IndexOutOfBoundsException() // Will be printed to the console by Thread.defaultUncaughtExceptionHandler
}
job.join()
println("Joined failed job")
val deferred = GlobalScope.async { // root coroutine with async
println("Throwing exception from async")
throw ArithmeticException() // Nothing is printed, relying on user to call await
}
try {
deferred.await()
println("Unreached")
} catch (e: ArithmeticException) {
println("Caught ArithmeticException")
}
}
|
import kotlinx.coroutines.*
func main() = runBlocking {
let job = GlobalScope.launch { // root coroutine with launch
print("Throwing exception from launch")
throw IndexOutOfBoundsException() // Will be printed to the console by Thread.defaultUncaughtExceptionHandler
}
job.join()
print("Joined failed job")
let deferred = GlobalScope.async { // root coroutine with async
print("Throwing exception from async")
throw ArithmeticException() // Nothing is printed, relying on user to call await
}
try {
deferred.await()
print("Unreached")
} catch (e: ArithmeticException) {
print("Caught ArithmeticException")
}
}
|
CoroutineExceptionHandler
|
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception")
}
val job = GlobalScope.launch(handler) { // root coroutine, running in GlobalScope
throw AssertionError()
}
val deferred = GlobalScope.async(handler) { // also root, but async instead of launch
throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await()
}
joinAll(job, deferred)
|
let handler = CoroutineExceptionHandler { _, exception ->
print("CoroutineExceptionHandler got $exception")
}
let job = GlobalScope.launch(handler) { // root coroutine, running in GlobalScope
throw AssertionError()
}
let deferred = GlobalScope.async(handler) { // also root, but async instead of launch
throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await()
}
joinAll(job, deferred)
|
Cancellation and exceptions
|
val job = launch {
val child = launch {
try {
delay(Long.MAX_VALUE)
} finally {
println("Child is cancelled")
}
}
yield()
println("Cancelling child")
child.cancel()
child.join()
yield()
println("Parent is not cancelled")
}
job.join()
|
let job = launch {
let child = launch {
try {
delay(Long.MAX_VALUE)
} finally {
print("Child is cancelled")
}
}
yield()
print("Cancelling child")
child.cancel()
child.join()
yield()
print("Parent is not cancelled")
}
job.join()
|
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception")
}
val job = GlobalScope.launch(handler) {
launch { // the first child
try {
delay(Long.MAX_VALUE)
} finally {
withContext(NonCancellable) {
println("Children are cancelled, but exception is not handled until all children terminate")
delay(100)
println("The first child finished its non cancellable block")
}
}
}
launch { // the second child
delay(10)
println("Second child throws an exception")
throw ArithmeticException()
}
}
job.join()
|
let handler = CoroutineExceptionHandler { _, exception ->
print("CoroutineExceptionHandler got $exception")
}
let job = GlobalScope.launch(handler) {
launch { // the first child
try {
delay(Long.MAX_VALUE)
} finally {
withContext(NonCancellable) {
print("Children are cancelled, but exception is not handled until all children terminate")
delay(100)
print("The first child finished its non cancellable block")
}
}
}
launch { // the second child
delay(10)
print("Second child throws an exception")
throw ArithmeticException()
}
}
job.join()
|
Exceptions aggregation
|
import kotlinx.coroutines.*
import java.io.*
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception with suppressed ${exception.suppressed.contentToString()}")
}
val job = GlobalScope.launch(handler) {
launch {
try {
delay(Long.MAX_VALUE) // it gets cancelled when another sibling fails with IOException
} finally {
throw ArithmeticException() // the second exception
}
}
launch {
delay(100)
throw IOException() // the first exception
}
delay(Long.MAX_VALUE)
}
job.join()
}
|
import kotlinx.coroutines.*
import java.io.*
func main() = runBlocking {
let handler = CoroutineExceptionHandler { _, exception ->
print("CoroutineExceptionHandler got $exception with suppressed ${exception.suppressed.contentToString()}")
}
let job = GlobalScope.launch(handler) {
launch {
try {
delay(Long.MAX_VALUE) // it gets cancelled when another sibling fails with IOException
} finally {
throw ArithmeticException() // the second exception
}
}
launch {
delay(100)
throw IOException() // the first exception
}
delay(Long.MAX_VALUE)
}
job.join()
}
|
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception")
}
val job = GlobalScope.launch(handler) {
val inner = launch { // all this stack of coroutines will get cancelled
launch {
launch {
throw IOException() // the original exception
}
}
}
try {
inner.join()
} catch (e: CancellationException) {
println("Rethrowing CancellationException with original cause")
throw e // cancellation exception is rethrown, yet the original IOException gets to the handler
}
}
job.join()
|
let handler = CoroutineExceptionHandler { _, exception ->
print("CoroutineExceptionHandler got $exception")
}
let job = GlobalScope.launch(handler) {
let inner = launch { // all this stack of coroutines will get cancelled
launch {
launch {
throw IOException() // the original exception
}
}
}
try {
inner.join()
} catch (e: CancellationException) {
print("Rethrowing CancellationException with original cause")
throw e // cancellation exception is rethrown, yet the original IOException gets to the handler
}
}
job.join()
|
Supervision job
|
import kotlinx.coroutines.*
fun main() = runBlocking {
val supervisor = SupervisorJob()
with(CoroutineScope(coroutineContext + supervisor)) {
// launch the first child -- its exception is ignored for this example (don't do this in practice!)
val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) {
println("The first child is failing")
throw AssertionError("The first child is cancelled")
}
// launch the second child
val secondChild = launch {
firstChild.join()
// Cancellation of the first child is not propagated to the second child
println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")
try {
delay(Long.MAX_VALUE)
} finally {
// But cancellation of the supervisor is propagated
println("The second child is cancelled because the supervisor was cancelled")
}
}
// wait until the first child fails & completes
firstChild.join()
println("Cancelling the supervisor")
supervisor.cancel()
secondChild.join()
}
}
|
import kotlinx.coroutines.*
func main() = runBlocking {
let supervisor = SupervisorJob()
with(CoroutineScope(coroutineContext + supervisor)) {
// launch the first child -- its exception is ignored for this example (don't do this in practice!)
let firstChild = launch(CoroutineExceptionHandler { _, _ -> }) {
print("The first child is failing")
throw AssertionError("The first child is cancelled")
}
// launch the second child
let secondChild = launch {
firstChild.join()
// Cancellation of the first child is not propagated to the second child
print("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")
try {
delay(Long.MAX_VALUE)
} finally {
// But cancellation of the supervisor is propagated
print("The second child is cancelled because the supervisor was cancelled")
}
}
// wait until the first child fails & completes
firstChild.join()
print("Cancelling the supervisor")
supervisor.cancel()
secondChild.join()
}
}
|
Supervision scope
|
import kotlin.coroutines.*
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
supervisorScope {
val child = launch {
try {
println("The child is sleeping")
delay(Long.MAX_VALUE)
} finally {
println("The child is cancelled")
}
}
// Give our child a chance to execute and print using yield
yield()
println("Throwing an exception from the scope")
throw AssertionError()
}
} catch(e: AssertionError) {
println("Caught an assertion error")
}
}
|
import kotlin.coroutines.*
import kotlinx.coroutines.*
func main() = runBlocking {
try {
supervisorScope {
let child = launch {
try {
print("The child is sleeping")
delay(Long.MAX_VALUE)
} finally {
print("The child is cancelled")
}
}
// Give our child a chance to execute and print using yield
yield()
print("Throwing an exception from the scope")
throw AssertionError()
}
} catch(e: AssertionError) {
print("Caught an assertion error")
}
}
|
Exceptions in supervised coroutines
|
import kotlin.coroutines.*
import kotlinx.coroutines.*
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception")
}
supervisorScope {
val child = launch(handler) {
println("The child throws an exception")
throw AssertionError()
}
println("The scope is completing")
}
println("The scope is completed")
}
|
import kotlin.coroutines.*
import kotlinx.coroutines.*
func main() = runBlocking {
let handler = CoroutineExceptionHandler { _, exception ->
print("CoroutineExceptionHandler got $exception")
}
supervisorScope {
let child = launch(handler) {
print("The child throws an exception")
throw AssertionError()
}
print("The scope is completing")
}
print("The scope is completed")
}
|