kotlinkotlin-coroutinescoroutine

Why setting parent coroutineContext.job to Job() does not cause children to be cancelled?


In the following code, I build a coroutine in scope1 using launch. I set its job to be instance of Job, so any children's cancellation will cause the parent job to be cancelled.

I used awaitAll which, according to documentation, "while this awaitAll fails immediately as soon as any of the deferreds fail."


val scope1 = CoroutineScope(Dispatchers.Default)
val result = IntArray(4)

fun main() = runBlocking {
    val parentJob = scope1.launch(Job()) {
        val childJobList = (1..4).map { i ->
            async {
                delay(i.toLong() * 1000.toLong())
                if (i == 2) {
                    cancel()
                }
                yield()
                println("async $i is done")
                result[i - 1] = i
            }
        }
        try {
            childJobList.awaitAll()
            println("Child job completed successfully")
        } catch (e: Exception) {
            println("Caught exception in parent: ${e.message}")
        }
    }

    parentJob.join()
    println("Parent job completed; result = ${result.contentToString()}")
}

However, it looks like that is not what happened. Running the code gives the following output:

async 1 is done
Caught exception in parent: DeferredCoroutine was cancelled
async 3 is done
async 4 is done
Parent job completed; result = [1, 0, 3, 4]

Can someone help explain why this is the case?


Solution

  • I'm not exactly sure what is the part that confuses you. awaitAll clearly failed as we can see by the "Caught exception in parent: DeferredCoroutine was cancelled" log. You caught the exception, so the parent coroutine didn't fail.

    Regarding this:

    any children's cancellation will cause the parent job to be cancelled.

    Cancellations don't propagate to parents, only "real" failures do. We can find multiple mentions on this in the documentation, for example:

    If a coroutine encounters an exception other than CancellationException, it cancels its parent with that exception.

    https://kotlinlang.org/docs/exception-handling.html#cancellation-and-exceptions

    BTW, adding Job() in the launch() doesn't really do anything in this example. If you believe this way we somehow enable exception propagation, then you must have entirely misunderstood something in the documentation.