kotlinx.coroutines
kotlinx.coroutines copied to clipboard
scope with job in completing state can still launch new coroutines
Version: 1.6
I was surprised to see that while a job is completing (i.e., waiting for all of its children to complete) new children can still be launched. In contrast, when a job is complete, new children get canceled automatically.
This forces me to keep track myself whether I'm draining a job and avoid calling launch if so.
Here is the repro:
fun main() = runBlocking {
val job = Job()
val scope = CoroutineScope(job)
val sema = Semaphore(1, 1)
scope.launch {
sema.acquire()
}
job.complete() // transitions to completing until the above coroutine completes
val c1 = scope.launch {
println("don't think this should run") // this runs
}
c1.join()
// cleanup
sema.release()
job.join()
}
We initially thought about alternatives to the current behaviour.
The biggest problem with the proposed one is that it's racy and may yield the unexpected result: everything is completed normally, but some children are cancelled for some reason, though there were neither failures nor explicit cancellations.
Racy-ness can also lead to unexpected behaviour when the parent job is not something to call .complete()
on, but have its own body to execute non-deterministically
unexpected result: everything is completed normally, but some children are cancelled for some reason
The Java analogue: when you shutdown() an executor, new tasks are rejected despite no failure having occurred. FYI my use-case is a server that launches a coroutine per request. While shutting down the server I need a way to drain requests without incoming requests preventing a full drain. With an executor, a call to shutdown() was all I needed. With coroutines for some reason I need to implement my own draining logic.
unexpected behaviour when the parent job is not something to call .complete() on, but have its own body to execute non-deterministically
I can't think of an example that isn't an abuse of structured concurrency. When the body of a parent job is finished and the job is completing, when is it useful to allow a new child to run? The existing children may need to launch new coroutines but they would create them as their own children, not as siblings.
A related issue reported via YouTrack: https://youtrack.jetbrains.com/issue/KT-60182
Note to self: above-linked YouTrack issue is my own