Real coroutine cancellation is cooperative; this static example models the same idea with a checked flag.

cooperative cancellation Cooperative code checks whether it should continue before doing the next unit of work.

Cancellation Flag Model

active
CancellationFlagModel.kt
Replay: real traced execution (multi-file project)
import kotlin.coroutines.Continuation
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.startCoroutine

suspend fun runStep(active: Boolean, name: String): String {
    return if (active) "ran:$name" else "skipped:$name"
}

fun main() {
    val active = true
    val log = mutableListOf<String>()

    val work: suspend () -> String = {
        log.add(runStep(active, "load"))
        log.add(runStep(active, "save"))
        log.joinToString("|")
    }

    work.startCoroutine(object : Continuation<String> {
        override val context = EmptyCoroutineContext

        override fun resumeWith(result: Result<String>) {
            log.add("done")
        }
    })

    println("active=$active")
    println("log=" + log.joinToString("|"))
}
import kotlin.coroutines.Continuation
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.startCoroutine

suspend fun runStep(active: Boolean, name: String): String {
    return if (active) "ran:$name" else "skipped:$name"
}

fun main() {
    val active = false
    val log = mutableListOf<String>()

    val work: suspend () -> String = {
        log.add(runStep(active, "load"))
        log.add(runStep(active, "save"))
        log.joinToString("|")
    }

    work.startCoroutine(object : Continuation<String> {
        override val context = EmptyCoroutineContext

        override fun resumeWith(result: Result<String>) {
            log.add("done")
        }
    })

    println("active=$active")
    println("log=" + log.joinToString("|"))
}
  1. active ← true, log ← [], work ← () -> kotlin.String

    9fun main() {10    val active→ true = true //@active=false11    val log→ [] = mutableListOf<String>()1213    val work→ () -> kotlin.String: suspend () -> String = {14        log.add(runStep(active, "load"))15        log.add(runStep(active, "save"))16        log.joinToString("|")17    }1819    work() -> kotlin.String.startCoroutine(object : Continuation<String> {20        override val context = EmptyCoroutineContext2122        override fun resumeWith(result: Result<String>) {23            log.add("done")24        }25    })
  2. suspend fun runStep(active: Boolean, name: String): String

    pass 1 of 2
    5suspend fun runStep(activetrue: Boolean, nameload: String): String {6    return if (activetrue) "ran:$nameload" else "skipped:$name"7}
  3. log.add(runStep(active, "load"))

    13val work: suspend () -> String = {14    log.add(runStep(active, "load"))15    log.add(runStep(active, "save"))16    log.joinToString("|")
  4. suspend fun runStep(active: Boolean, name: String): String

    pass 2 of 2
    5suspend fun runStep(activetrue: Boolean, namesave: String): String {6    return if (activetrue) "ran:$namesave" else "skipped:$name"7}
  5. log.add(runStep(active, "save"))

    14log.add(runStep(active, "load"))15log.add(runStep(active, "save"))16log.joinToString("|")
  6. override fun resumeWith(result: Result<String>)

    19work() -> kotlin.String.startCoroutine(object : Continuation<String> {20    override val context = EmptyCoroutineContext2122    override fun resumeWith(resultSuccess(ran:load|ran:save): Result<String>) {23        log[ran:load, ran:save].add("done")24    }25})
  7. println("active=$active")

    27    println("active=$activetrue")28    println("log=" + log[ran:load, ran:save, done].joinToString("|"))29}
    outputactive=true
    log=ran:load|ran:save|done
  1. active ← false, log ← [], work ← () -> kotlin.String

    9fun main() {10    val active→ false = false11    val log→ [] = mutableListOf<String>()1213    val work→ () -> kotlin.String: suspend () -> String = {14        log.add(runStep(active, "load"))15        log.add(runStep(active, "save"))16        log.joinToString("|")17    }1819    work() -> kotlin.String.startCoroutine(object : Continuation<String> {20        override val context = EmptyCoroutineContext2122        override fun resumeWith(result: Result<String>) {23            log.add("done")24        }25    })
  2. suspend fun runStep(active: Boolean, name: String): String

    pass 1 of 2
    5suspend fun runStep(activefalse: Boolean, nameload: String): String {6    return if (activefalse) "ran:$nameload" else "skipped:$name"7}
  3. log.add(runStep(active, "load"))

    13val work: suspend () -> String = {14    log.add(runStep(active, "load"))15    log.add(runStep(active, "save"))16    log.joinToString("|")
  4. suspend fun runStep(active: Boolean, name: String): String

    pass 2 of 2
    5suspend fun runStep(activefalse: Boolean, namesave: String): String {6    return if (activefalse) "ran:$namesave" else "skipped:$name"7}
  5. log.add(runStep(active, "save"))

    14log.add(runStep(active, "load"))15log.add(runStep(active, "save"))16log.joinToString("|")
  6. override fun resumeWith(result: Result<String>)

    19work() -> kotlin.String.startCoroutine(object : Continuation<String> {20    override val context = EmptyCoroutineContext2122    override fun resumeWith(resultSuccess(skipped:load|skipped:save): Result<String>) {23        log[skipped:load, skipped:save].add("done")24    }25})
  7. println("active=$active")

    27    println("active=$activefalse")28    println("log=" + log[skipped:load, skipped:save, done].joinToString("|"))29}
    outputactive=false
    log=skipped:load|skipped:save|done