How does coroutine cancellation work internally?

This tests your grasp of cooperative cancellation. Explain that cancel() sets a flag, causing a CancellationException at the next suspension point. A non-suspending loop must explicitly check isActive to stop.
What's really being asked
This question probes your understanding of the core principle of coroutine cancellation: it is cooperative, not preemptive. The interviewer is checking if you know that a coroutine must actively participate in its own cancellation. This distinguishes a senior candidate who understands the mechanics from someone who incorrectly equates job.cancel() with killing a thread or process.
The full answer
First, the job.cancel() call itself is non-blocking. It transitions the Job to a 'Cancelling' state, propagates the cancellation request to all its children jobs, and returns immediately. It does not stop anything on its own.
Second, the actual stop happens via a CancellationException. All suspending functions built into kotlinx.coroutines (like delay, yield, withContext) are 'cooperative'. Internally, they check the coroutine's job status before and during suspension. If the job is in the 'Cancelling' state, they throw a CancellationException instead of resuming normally.
Third, for long-running computations without suspension points, the coroutine must manually cooperate. A tight loop performing CPU-bound work will ignore a cancellation request because it never calls a cooperative suspend function. To fix this, the loop must periodically check the isActive property of its CoroutineScope and exit if it becomes false.
The mistakes people make
Saying that job.cancel() stops the underlying thread. This is the most common and significant red flag. Coroutines are abstractions over threads, and cancellation is a feature of the coroutine's state machine, not the thread it happens to be running on.
Stating that execution stops immediately. Cancellation is a request. The coroutine will continue to run until it reaches the next suspension point or an isActive check. Any code in a finally block will still execute for cleanup.
Describing catching and swallowing a CancellationException. While you can catch it to perform cleanup (e.g., closing a file), it must be re-thrown. Swallowing it effectively ignores the cancellation request, leaving the coroutine and its parent in an inconsistent state.
What usually comes next
How would you make a long-running, non-suspending function cancellable? The answer is to pass in the CoroutineScope and periodically check isActive inside the function's loop.
What is the difference between cancel() and cancelAndJoin()? cancel() sends the request and moves on. cancelAndJoin() sends the request and then suspends until the coroutine has fully completed its cancellation process, including any finally blocks.
What is NonCancellable used for? It's a context used to run suspending code that must not be cancelled, such as critical cleanup operations like writing a file to disk or committing a database transaction in a finally block.
A concrete example
Imagine a coroutine launched with val job = launch { ... }. Inside, it has a loop: while (i < 1_000_000_000) { i++ }. If you call job.cancel(), this coroutine will not stop. It will run to completion because it never suspends or checks its state. The correct, cooperative version is while (isActive && i < 1_000_000_000) { i++ }. Now, when job.cancel() is called, isActive will eventually become false, and the loop will terminate gracefully.
Interview question
Which statement accurately describes how job.cancel() influences a running coroutine?
- a.It blocks until the coroutine reaches a safe point and then gracefully shuts it down.
- b.It sets a flag, causing cooperative suspend functions or explicit isActive checks to throw a CancellationException.Correct
- c.It forces the coroutine to yield its execution, allowing other coroutines to run until it is eventually garbage collected.
- d.It immediately terminates the coroutine's execution by stopping the underlying thread.
Why? this is the answer
The correct answer (B) reflects that cancellation is cooperative: job.cancel() sets a flag, and the coroutine itself must check this flag (via suspend functions or isActive) to throw a CancellationException. Option (D) is a common misconception, as coroutine cancellation does not directly stop the underlying thread; it's an abstraction above threads.
Just read this? Test yourself on what you have been reading.
Read the original → kotlinlang.org
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on kotlin — each one lists the topics its interview covers.
See open roles