Breaking down a simple case on Android to suspend the main thread and perform concurrent processing with coroutines, the following code only prints Launched and runBlocking never completes:
runBlocking {
val ioJob = launch(Dispatchers.IO) {
delay(1000)
}
val mainJob = launch(Dispatchers.Main.immediate) {
Log.d("Routine", "Launched")
ioJob.join()
Log.d("Routine", "Joined")
}
listOf(mainJob, ioJob).joinAll()
}
Of course if we replace Dispatchers.Main.immediate with Dispatchers.IO everything works great, but some of my concurrent processing should be run on main. Using Dispatchers.Main doesn't log anything, as expected. It appears that once a join is performed within the root of runBlocking it paralyzes anything suspended that was dispatched to main.
Worth noting, an approach predicated on CountDownLatch and threads works great:
val latchMain = CountDownLatch(1)
val primaryLatch = CountDownLatch(2)
val ioExecutor = Executors.newCachedThreadPool(Executors.defaultThreadFactory())
log("Execute IO")
ioExecutor.execute(
Runnable {
log("Delay IO")
Thread.sleep(1000)
log("Countdown Main")
latchMain.countDown()
Thread.sleep(3000)
primaryLatch.countDown()
})
log("Execute Main")
Runnable {
log("Await Main")
latchMain.await()
log("Countdown Primary")
primaryLatch.countDown()
}.run()
log("Await Primary")
primaryLatch.await()
log("Continue")
stepTracker.endTracking()
return stepTracker.stepGraphTrace
}
private fun log(msg: String) = Log.i("Routine", "[${Thread.currentThread().name}] $msg")
With output:
2021-08-11 11:04:06.508 [main] Execute IO
2021-08-11 11:04:06.509 [main] Execute Main
2021-08-11 11:04:06.510 [main] Await Main
2021-08-11 11:04:06.510 [pool-25-thread-1] Delay IO
2021-08-11 11:04:07.512 [pool-25-thread-1] Countdown Main
2021-08-11 11:04:07.513 [main] Countdown Primary
2021-08-11 11:04:07.513 [main] Await Primary
2021-08-11 11:04:10.514 [main] Continue
Any ideas? About to go submit an issue to JetBrains on this one.
