`coroutineContext` is a compiler-provided property available inside a suspending function. It exposes the `CoroutineContext` of the coroutine currently running that function. Think of that context as a set of elements describing how the coroutine runs and how it participates in its lifetime: commonly a dispatcher and a `Job`, plus optional elements such as `CoroutineName` and `CoroutineExceptionHandler`. section 14.8
import kotlin.coroutines.coroutineContext
suspend fun introspect() {
log(coroutineContext) #1
}
fun main() {
runBlocking {
introspect()
}
}
// 25 [main @coroutine#1] [CoroutineId(1),
"coroutine#1":BlockingCoroutine{Active}@610694f1,
BlockingEventLoop@43814d18]
Read `coroutineContext` directly from a suspending function when inspecting the active coroutine. The logged value reveals its installed context elements. section 14.8
A dispatcher argument passed to `runBlocking`, `launch`, `async`, or `withContext` is really a `CoroutineContext` argument. Supplying an element replaces that element in the child context; combine several elements with `+`. This lets one call set both execution policy and useful metadata. section 14.8 section 14.9
Override selected context elements
```kotlin fun main() { runBlocking(Dispatchers.IO + CoroutineName("Coolroutine")) { introspect() } } ``` Here `Dispatchers.IO` replaces the dispatcher otherwise used by `runBlocking`, while `CoroutineName("Coolroutine")` adds a name. Other inherited elements remain available unless replaced by another element with the same role. section 14.8
Contexts follow the structured-concurrency hierarchy. A newly started child first inherits its parent context, then receives a new `Job` linked to the parent job, and finally applies any context arguments supplied at creation. Consequently, a `launch` without a dispatcher does not automatically use `Dispatchers.Default`; it uses the parent dispatcher. section 15.1.4
fun main() {
runBlocking(Dispatchers.Default) {
log(coroutineContext)
launch {
log(coroutineContext)
launch(Dispatchers.IO + CoroutineName("mine")) {
log(coroutineContext)
}
}
}
}
// 0 [DefaultDispatcher-worker-1 @coroutine#1] [CoroutineId(1),
"coroutine#1":BlockingCoroutine{Active}@68308697, Dispatchers.Default]
// 1 [DefaultDispatcher-worker-2 @coroutine#2] [CoroutineId(2),
"coroutine#2":StandaloneCoroutine{Active}@2b3ce773, Dispatchers.Default]
// 2 [DefaultDispatcher-worker-3 @mine#3] [CoroutineName(mine),
CoroutineId(3), "mine#3":StandaloneCoroutine{Active}@7c42841a,
Dispatchers.IO]
Compare the logged contexts: the first child inherits `Dispatchers.Default`, while the nested child overrides that dispatcher with `Dispatchers.IO` and receives its own name. section 15.1.4
Use `coroutineContext` to reason about these practical concerns:
- Inspect the current `Job`, including its `parent` and `children`, to make the coroutine hierarchy visible. section 15.1.4
- Understand cancellation boundaries: the `Job` in the context establishes the parent-child relationship used by structured concurrency. section 15.3
- Attach a `CoroutineExceptionHandler` to customize last-resort processing of uncaught exceptions at a root coroutine. section 18.3 section 18.6
A context is not merely a thread choice. It combines execution information, lifecycle structure, names, and optional failure handling. Inspect it from suspending code, inherit it deliberately through coroutine hierarchies, and override only the elements a child genuinely needs. section 14.8 section 15.1.4
