What do we have now?
The following deadlocks because nested runBlocking tries to process tasks from the outer runBlocking, and one of the tasks actually depends on the completion of the nested runBlocking:
import kotlinx.coroutines.*
class Container(private val scope: CoroutineScope) {
private val instances = HashMap<Class<*>, Deferred<Any>>() // basically a map
suspend fun <T> instance(key: Class<out T>): T {
return instances
.computeIfAbsent(key, ::instanceDeferred)
.await() as T
}
private fun instanceDeferred(clazz: Class<*>): Deferred<Any> = scope.async {
val constructor = clazz.getConstructor(Container::class.java)
constructor.isAccessible = true
constructor.newInstance(this@Container)
}
}
class MyService(container: Container) {
init {
runBlocking {
// this can ask container for an unrelated service,
// or in this case it's enough for it to be empty
}
}
}
class MyService2(container: Container) {
val service1 = runBlocking {
container.instance(MyService::class.java)
}
}
fun main() {
runBlocking {
val container = Container(this@runBlocking)
launch {
println(container.instance(MyService::class.java))
}
launch {
println(container.instance(MyService2::class.java).service1)
}
}
}
What should be instead?
runBlocking should not steal tasks from outer runBlocking.
Alternatively, please provide an API to make runBlocking not steal tasks. It may be a boolean, or a special coroutine context element. Currently we have to use internals.
Why?
Because otherwise it deadlocks.
Notes
Originally, the "stealing" was added to fix a very specific use-case #860.
I believe that if nested runBlocking "steals" tasks while being inside a limited dispatcher, then this stealing logic should apply to all limited dispatchers, i.e. runBlocking on a thread inside Dispatchers.Default should steal tasks from Dispatchers.Default (#3439). If that's not possible for any reason, then nested runBlocking should not steal tasks from the outer runBlocking for the very same reason whatever it might be.
What do we have now?
The following deadlocks because nested
runBlockingtries to process tasks from the outerrunBlocking, and one of the tasks actually depends on the completion of the nestedrunBlocking:What should be instead?
runBlockingshould not steal tasks from outerrunBlocking.Alternatively, please provide an API to make
runBlockingnot steal tasks. It may be a boolean, or a special coroutine context element. Currently we have to use internals.Why?
Because otherwise it deadlocks.
Notes
Originally, the "stealing" was added to fix a very specific use-case #860.
I believe that if nested
runBlocking"steals" tasks while being inside a limited dispatcher, then this stealing logic should apply to all limited dispatchers, i.e.runBlockingon a thread insideDispatchers.Defaultshould steal tasks fromDispatchers.Default(#3439). If that's not possible for any reason, then nestedrunBlockingshould not steal tasks from the outerrunBlockingfor the very same reason whatever it might be.