Claude Brisson
12/18/2022, 3:02 PMsuspend fun someExternalSuspendFun(): String
fun returnResult(): String {
return runBlocking {
async {
return@async someExternalSuspendFun()
}.await()
}
}
Is there a simpler way to do this?efemoney
12/18/2022, 4:07 PMkevin.cianfarini
12/18/2022, 5:22 PMRuns a new coroutine and blocks the current thread until its completion. This function should not be used from a coroutine. It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in main functions and in tests.
kevin.cianfarini
12/18/2022, 5:23 PMwithContext(singleThreadedDispatcher)
Claude Brisson
12/18/2022, 6:20 PMClaude Brisson
12/18/2022, 6:24 PMkevin.cianfarini
12/18/2022, 6:48 PMval mySingleThreadedDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
...
withContext(mySingleThreadedDispatcher) { someTemplateStuff() }
kevin.cianfarini
12/18/2022, 6:52 PMclass SuspendingTemplateThing(private val templateThing: TemplateThing) {
private val singleThreadContext = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
suspend fun doAThing() = withContext(singleThreadContext) { templateThing.doAThing() }
}
This ensures your suspending functions are main safe.kevin.cianfarini
12/18/2022, 7:05 PMrunBlocking
from a suspend function because iirc it can cause deadlock. If the docs mention you shouldn't do it, I doubt your use case is safe from the reasons they warn against using it.
Furthermore, runBlocking
will block the current thread. If you happen to be calling the templating thing from multiple different coroutines all running on different threads (such as with <http://Dispatchers.IO|Dispatchers.IO>
) then each runBlocking call to the templating thing would happen on more than one thread.Nick Allen
12/18/2022, 11:37 PMrunBlocking
is safe or not. The warning against using it may be accurate or this could be exactly the scenario that runBlocking
was designed for. It really depends on what code is calling returnResult
, the thread that it's runnng on.
If returnResult
is a callback from a Java class/library that calls its callbacks from a dedicated thread for callbacks and expects callbacks to block, then you are using runBlocking
as intended.
There is a gotcha even in the intended use-case that if it's possible for other code to post to that thread then a dispatcher could exist that posts to that thread and then you could potentially deadlock (runBlocking
calls suspend method that uses said dispatcher that queues coroutine to thread blocked by runBlocking
).kevin.cianfarini
12/19/2022, 1:10 AMreturnResult
above is run within a coroutine, which I believe it is since this is all happening in the context of a Ktor request, then runBlocking is not safe to use I don't believe.Nick Allen
12/19/2022, 7:32 AM// suspending wrapper of non-suspend API
suspend fun SingleThreadedClass.printStuff(getMessage: suspend () -> String) {
suspendCoroutine { cont ->
printStuff(
//Using runBlocking here:
getMessage = { runBlocking { getMessage() }},
onDone = { cont.resume(Unit) }
)
}
}
class SingleThreadedClass() {
private val executor = ThreadPoolExecutor(0, 1, 0, TimeUnit.MILLISECONDS, LinkedBlockingQueue())
/**
* Executes block on private background thread to determine what to print.
*/
fun printStuff(getMessage: () -> String, onDone:() -> Unit) {
executor.execute {
println(getMessage())
onDone()
}
}
}
While runBlocking
is being used from code that is part of a coroutine, it is not being called from a thread that is part of dispatching coroutines. When the docs say "This function should not be used from a coroutine" it means that it should not be called if a suspend function is on the call-stack. When execution moves to the private executor thread, there's no coroutine on the callstack . This sort of situation where a callback is called from a private thread(pool) is exactly what runBlocking
is intended for.
I could see this sort of situation being the case depending on the details of the "Java single threaded templating rendering" but it depends.kevin.cianfarini
12/19/2022, 4:05 PMJava single-threaded template renderingto mean that the templating engine isn't thread safe and must be confined to a single thread. In your above example if the templating engine is a worker of sorts then yes, I believe you're correct, runBlocking would be fine. Regardless it seems like it might be easier to decouple these two concerns. Separately fetch the DB data and pass that data to the templating engine and you wouldn't need to worry about concurrency semantics here.