How do I cancel a coroutine in Kotlin safely?

In Kotlin, you cancel a coroutine safely by calling cancel() on its Job or scope, and making sure the coroutine code is cooperative: it should suspend regularly or check for cancellation.

Basic example

import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        try {
            repeat(1_000) { i ->
                println("Working $i")
                delay(500) // cancellable suspension point
            }
        } finally {
            println("Cleaning up")
        }
    }

    delay(1_500)
    job.cancel()
    job.join()

    println("Cancelled safely")
}

Or more commonly:

job.cancelAndJoin()
job.cancelAndJoin()

This cancels the coroutine and waits until it finishes cleanup.

Use cancellable suspending functions

Most kotlinx.coroutines suspending functions are cancellable, for example:

delay(...)
withContext(...)
receive(...)
send(...)

So this is usually safe:

val job = scope.launch {
    while (true) {
        delay(1000)
        doWork()
    }
}

job.cancel()

For CPU-heavy loops, check cancellation manually

If your coroutine does not suspend often, cancellation will not be noticed immediately.

Use isActive:

val job = scope.launch {
    while (isActive) {
        doCpuWorkChunk()
    }
}

Or call ensureActive():

val job = scope.launch {
    while (true) {
        ensureActive()
        doCpuWorkChunk()
    }
}

Cleanup with finally

Use try/finally for cleanup:

val job = scope.launch {
    try {
        doWork()
    } finally {
        closeResources()
    }
}

If cleanup needs to call suspending functions, use NonCancellable:

val job = scope.launch {
    try {
        doWork()
    } finally {
        withContext(NonCancellable) {
            saveState()
            closeRemoteConnection()
        }
    }
}

Do not swallow CancellationException

Cancellation is represented by CancellationException. Avoid catching it accidentally and ignoring it.

Bad:

try {
    doWork()
} catch (e: Exception) {
    // This also catches CancellationException
    logError(e)
}

Better:

try {
    doWork()
} catch (e: CancellationException) {
    throw e
} catch (e: Exception) {
    logError(e)
}

Cancel a scope

If you created a scope, cancel it when the owner is destroyed:

class Repository {
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

    fun start() {
        scope.launch {
            doWork()
        }
    }

    fun close() {
        scope.cancel()
    }
}

Summary

Use:

job.cancelAndJoin()

Make your coroutine cooperative by using:

delay(...)
isActive
ensureActive()

Clean up with:

try {
    // work
} finally {
    // cleanup
}

And avoid swallowing CancellationException.

How do I launch a coroutine in Kotlin using launch and runBlocking?

In Kotlin, you can use runBlocking to start a coroutine scope that blocks the current thread until its coroutines finish, and launch to start a new coroutine inside that scope.

import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    launch {
        println("Coroutine is running")
    }

    println("Main coroutine continues")
}

Output may look like:

Main coroutine continues
Coroutine is running

runBlocking creates a coroutine and blocks the current thread until all child coroutines complete.

launch starts a new coroutine that runs concurrently with the rest of the code inside the runBlocking scope.

A slightly clearer example:

import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    launch {
        delay(1000)
        println("World!")
    }

    println("Hello")
}

Output:

Hello
World!

Here:

  • runBlocking { ... } starts a blocking coroutine scope.
  • launch { ... } starts a child coroutine.
  • delay(1000) suspends the coroutine for 1 second without blocking the thread.
  • runBlocking waits until the launched coroutine finishes before main exits.