Lesson 17 / 25
Structured Concurrency and Cancellation
Scopes, Jobs, cancellation and failure propagation.
Every coroutine has a parent
With structured concurrency, coroutines are launched in a CoroutineScope, and a parent does not complete until all its children complete. If a child fails, the failure cancels the parent and its other children by default; supervisorScope or a SupervisorJob lets siblings fail independently. Cancelling a scope cancels all its children, which prevents leaked work, for example viewModelScope on Android is cancelled when the ViewModel is cleared. Cancellation is cooperative: suspending functions from kotlinx.coroutines check for it and throw CancellationException; CPU-heavy loops should call ensureActive() or yield(). Do not swallow CancellationException in catch blocks, and use withTimeout for deadlines. Avoid GlobalScope, which escapes structure.
Cancellation and timeouts
Cooperative cancellation in practice.
import kotlinx.coroutines.*
suspend fun crunch(items: List<Int>): Long = withContext(Dispatchers.Default) {
var sum = 0L
for (item in items) {
ensureActive() // honour cancellation in a CPU loop
sum += item.toLong() * item
}
sum
}
suspend fun loadWithDeadline(): String? =
withTimeoutOrNull(2_000) { // null if it takes longer than 2 seconds
delay(500)
"loaded"
}
class Worker(private val scope: CoroutineScope) {
private var job: Job? = null
fun start() { job = scope.launch { while (isActive) { delay(1_000) /* poll */ } } }
fun stop() { job?.cancel() }
}Rethrow CancellationException
A blanket catch (e: Exception) inside a coroutine also catches CancellationException. Rethrow it (or catch narrower exception types) so cancellation still propagates.
Quick check: What happens to child coroutines when their scope is cancelled?
- They are restarted
- They keep running in GlobalScope
- They are cancelled too
- Nothing; children are independent of scopes
Answer
They are cancelled too — Structured concurrency ties child lifetimes to the parent.