Cooperative Cancellation: isActive and ensureActive
Make coroutines cancellable by checking isActive and calling ensureActive.
Cooperative Cancellation: isActive and ensureActive is a free Kotlin Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Cooperative Cancellation?
Kotlin coroutines use cooperative cancellation: a cancelled coroutine is only stopped when it cooperates by checking its cancellation state. A tight CPU loop will not stop until it checks.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
var i = 0
while (true) { i++ } // never checks — never cancels!
}
delay(100)
job.cancel()
println("cancel sent but loop kept running until process ended")
}Checking isActive
isActive is a property on CoroutineScope that returns false once cancellation is requested. Checking it in a loop makes the coroutine cooperate.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
var i = 0
while (isActive) {
i++
}
println("Loop exited after $i iterations")
}
delay(10)
job.cancel()
job.join()
}ensureActive()
ensureActive() throws CancellationException if the coroutine is cancelled. It is a drop-in replacement for if (!isActive) throw CancellationException().
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
repeat(1_000_000) {
ensureActive() // throws if cancelled
// heavy computation here
}
} catch (e: CancellationException) {
println("Cancelled at iteration")
throw e
}
}
delay(5)
job.cancel()
job.join()
}yield() as a Cooperative Point
yield() suspends the coroutine briefly, allowing the scheduler to check cancellation and run other coroutines.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
repeat(10) { i ->
yield() // cooperative cancellation point + gives up scheduler
println("Step $i")
}
}
delay(2)
job.cancel()
job.join()
println("Done")
}isActive vs ensureActive
Use isActive for conditional logic (e.g., break out of a loop gracefully). Use ensureActive() when you want to propagate cancellation as an exception immediately.
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
repeat(100) { i ->
if (!isActive) {
println("Stopping at $i, cleaning up...")
return@launch
}
// work
}
}.also { delay(5); it.cancel(); it.join() }
}CancellationException is Special
CancellationException is not propagated to the parent — it signals normal cancellation. Never swallow it; always rethrow or let it propagate.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
delay(1000)
} catch (e: CancellationException) {
println("Caught cancellation, rethrowing")
throw e // important!
}
}
delay(50)
job.cancel()
job.join()
println("Job state: ${job.isCancelled}")
}Cancellation in CPU-Bound Work
For CPU-bound work, insert ensureActive() or yield() at natural checkpoints to allow timely cancellation.
import kotlinx.coroutines.*
suspend fun heavyCompute(n: Int): Long {
var result = 0L
for (i in 0..n) {
ensureActive()
result += i
}
return result
}
fun main() = runBlocking {
val job = launch(Dispatchers.Default) {
println(heavyCompute(1_000_000))
}
delay(10)
job.cancel()
job.join()
}isActive in a Custom Suspend Function
You can access isActive inside any suspend function via coroutineContext.isActive or by using currentCoroutineContext().isActive.
import kotlinx.coroutines.*
suspend fun checkable() {
while (currentCoroutineContext().isActive) {
delay(10)
println("tick")
}
}
fun main() = runBlocking {
val job = launch { checkable() }
delay(35)
job.cancel()
job.join()
}Cancellation with Resource Cleanup
Even with cooperative cancellation, resources can be cleaned up in finally blocks — cancellation exception still propagates through them.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
try {
repeat(100) { i ->
ensureActive()
delay(20)
println("Working $i")
}
} finally {
println("Cleanup done")
}
}
delay(50)
job.cancel()
job.join()
}Polling with isActive
Polling loops are a perfect fit for isActive: keep polling until the coroutine is cancelled.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
while (isActive) {
println("Polling...")
delay(100)
}
println("Poller stopped")
}
delay(350)
job.cancel()
job.join()
}Difference from Thread.interrupted()
Unlike Java threads, Kotlin coroutines do not rely on interrupt flags. Cancellation is modeled via exceptions and scope state, making it composable and structured.
import kotlinx.coroutines.*
fun main() = runBlocking {
// Kotlin way: cooperative via isActive / ensureActive
launch {
ensureActive()
println("Kotlin coroutine: cancellation is explicit and structured")
}
}Quick Check
Which function throws immediately when a coroutine is cancelled?
Recap
Cooperative cancellation requires explicit check points: isActive for conditional exit, ensureActive() to throw immediately, yield() to suspend and give up the thread. Never swallow CancellationException.
Frequently asked questions
Is the “Cooperative Cancellation: isActive and ensureActive” lesson free?
Yes — the full text of “Cooperative Cancellation: isActive and ensureActive” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.
What will I learn in “Cooperative Cancellation: isActive and ensureActive”?
Make coroutines cancellable by checking isActive and calling ensureActive. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Kotlin Academy?
No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Cooperative Cancellation: isActive and ensureActive” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Kotlin Academy lesson?
Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Cooperative Cancellation: isActive and ensureActive
- withTimeout and withTimeoutOrNull
- Cleanup with finally and NonCancellable
- Cancellation Propagation in Coroutine Hierarchies