Abhi
03/02/2024, 1:51 PMfun main() = runBlocking<Unit> {
launch {
launch {
launch {
delay(1000)
}
delay(1000)
}
delay(1000)
}
}
Jeff Lockhart
03/02/2024, 5:04 PMlaunch
calls are being called from the same CoroutineScope
receiver from runBlocking
. Each call to launch
creates a new coroutine with its own Job
, which is returned by the call. That Job
is a child of the parent runBlocking
scope's Job
.
If you were to include additional context in the launch
calls, e.g. launch(Dispatchers.Main + CoroutineName("coroutine-A")) { ...
, that context would be merged with the parent scope's context from runBlocking
(by default here, just EmptyCoroutineContext
), which would be the coroutine's parent context. That parent context plus the child Job
is equal to the child context, which makes up the child scope.
It's the Job
that defines when a CoroutineScope
is cancelled. And that Job
potentially has a parent/child relationship with other `Job`s in order to propagate cancellation. A SupervisorJob
is unique in that it does not propagate its cancellation. For this reason, a top-level CoroutineScope
is often defined with a SupervisorJob
.Jeff Lockhart
03/02/2024, 5:10 PMfun main() = runBlocking<Unit>(CoroutineName("foo")) {
// Dispatchers.Main + CoroutineName("foo") + job1
val job1 = launch(Dispatchers.Main) {
// CoroutineName("bar") + job2
val job2 = launch(CoroutineName("bar")) {
// CoroutineName("bar") + job3
val job3 = launch {
delay(1000)
}
delay(1000)
}
delay(1000)
}
}
Abhi
03/03/2024, 10:33 AMAbhi
03/03/2024, 10:35 AMfun main(): Unit = runBlocking {
launch(CoroutineName("foo")) {
launch(CoroutineName("bar")) {
launch {
println(coroutineContext[CoroutineName])
}
}
}
}
Abhi
03/03/2024, 10:35 AMAbhi
03/03/2024, 2:47 PMJeff Lockhart
03/03/2024, 3:03 PMfor job3 = launch { }, it would be // CoroutineName("bar") + job3?
Ah, yes. This is a good point because it shows how the parent context comes from the coroutine being launched from.