1
votes

Consider this non cancellable coroutine that works as its name implies.

fun main(args: Array<String>) = runBlocking {

    val nonCancellableJob = launch(Dispatchers.Default) {
        for (i in 1..1000) {
            if (i % 100 == 0) {
                println("Non cancellable iteration $i")
            }
        }
    }

    println("Cancelling non cancellable job...")
    nonCancellableJob.cancelAndJoin()
}

Now, if I get rid of the explicit dispatcher Dispatchers.Default and use the inherited one i.e. launch {...} the coroutine gets cancelled immediately without printing anything. It seems that a non cancelling coroutine is being cancelled! Is it a bug or what?

2

2 Answers

0
votes

When you don't use Dispatchers.Default then launch is inheriting dispatcher of runBlocking which seems to defer execution until needed.

Even though your coroutine cannot be canceled while running (since it doesn't have any activity check nor does it have suspension points) it's still possible to cancel it before it starts executing.

You can modify CoroutineStart of your launch builder to alter this behavior:

To execute it immediately when declared:

val nonCancellableJob = launch(start = CoroutineStart.UNDISPATCHED) { ... }

To prevent cancellation before it starts:

val nonCancellableJob = launch(start = CoroutineStart.ATOMIC) { ... }
0
votes

The inherited dispatcher runs on the same thread where you launched it. This works such that runBlocking establishes a top-level loop that goes over the coroutines running inside it, resuming them one by one. It can't resume the next coroutine before the current one has suspended itself.

In your code, the top-level runBlocking coroutine created one child coroutine and then, without suspending, went on to do some more things: print a message and cancel the coroutine it just created. At this point the coroutine is still hanging in the limbo of having been created but not run. Your cancelAndJoin call cancels it in this state, before it has got its chance to hog the runBlocking dispatcher forever. It immediately changes the state to "completed through cancellation" and the whole program ends.