Does NonCancellable "swallow" cancellations?

If I have a coroutine that contains a withContext(NonCancellable) {} block, and said coroutine is cancelled while execution is inside that block, what is supposed to happen?

I tried this in my code, and expected that the cancellation is somehow “remembered” after the NonCancellable block is finished. That is - as soon as the block is left, the coroutine stops. But instead, it appears to me that the cancellation is somehow forgotten, as if that block caused it to be dropped…

Does anybody know more? I even tried to check isActive and isCancelled, and both were true and false, respectively.

2 Likes

When you call withContext with a Job (like NonCancellable, it simply replaces any previous Job in the new CoroutineContext.

The new CoroutineContext has no relationship with the old Job.

When you cancel NonCancellable, it simply ignores the call. If you want to cancel the previous Job/CoroutineScope you need to cancel it directly.

But this only affects the code within the withContext block, not the code outside of it, right?

Example:

val job = launch {
    withContext(NonCancellable) {
        // ...
    }
    delay(10000)
}

If some other code calls job.cancel() while the coroutine is currently running the block inside withContext, then after that block is finished, the delay call still should exit immediately because job was cancelled, right?

1 Like

Yes-ish. Technically, the withContext will run to completion and then resume with a CancellationException so delay won’t event be called. This is referred to in the docs as a “prompt cancellation guarantee”.

The docs have pretty detailed info about what happens in this case: withContext