Kieran Wallbanks
09/05/2022, 4:31 PMrunBlocking
, I read that it shouldn't be used from a coroutine due to the potential for deadlocks (and we do seem to be encountering deadlocks here) but is there any way to "detect" if you're inside a coroutine somehow and run suspending methods in that coroutine instead of using runBlocking
? For reference, we're working inside a lot of pre-existing Java code that obviously can't be suspending, so I'm not sure how else to bridge it other than runBlocking
, which obviously isn't great and doesn't seem to be working for us.Marc Knaup
09/05/2022, 4:34 PMrunBlocking
to execute non-blocking coroutine code inside blocking non-coroutine code.
To run blocking code from within a coroutine you’d use the opposite, for example:
withContext(Dispatchers.Default) {
// blocking stuff
}
Kieran Wallbanks
09/05/2022, 4:35 PMwithContext
. For example
override fun someJavaApiImpl() {
runBlocking { someKotlinSuspendingMethod() }
}
Adam Powell
09/05/2022, 4:36 PMJoffrey
09/05/2022, 4:39 PMrunBlocking
is fine in places where you cannot make your function suspend
(like implementing java interfaces) and a result is synchronously expected from you (or the work needs to be completed synchronously before returning) - you don't really have a choice in those cases.
If a result is not expected from you (or the work does not need to be completed synchronously), and basically you can just "fire and forget", then it's another story. This means you can return from the non-suspending function before the work is done.Kieran Wallbanks
09/05/2022, 4:42 PMrunBlocking
is fine to use and doesn't cause issues where it can deadlock if you use it inside places we can't use suspend
but are done from suspending functions?Adam Powell
09/05/2022, 4:43 PMrunBlocking
and look for a different solution insteadJoffrey
09/05/2022, 4:44 PMand doesn't cause issues where it can deadlockThat's not what I meant, sorry if it was unclear. It can still cause issues if you're blocking the very thread that you're trying to dispatch on, for instance. What I meant is that you don't have a choice in that setup, and probably you should re-organize the calls differently if you really end up in deadlock situations.
Adam Powell
09/05/2022, 4:44 PMrunBlocking
shines is usually in cases that are entry points of some kind; @Test
methods, request handlers for server frameworks that dedicate a thread per request, things like thatrunBlocking
probably shouldn't appear in your production code at allJoffrey
09/05/2022, 4:44 PMAdam Powell
09/05/2022, 4:45 PMJoffrey
09/05/2022, 4:46 PMsuspendCancellableCoroutine
or callbackFlow
Kieran Wallbanks
09/05/2022, 4:50 PMIt can still cause issues if you're blocking the very thread that you're trying to dispatch on, for instance.Could you expand on what you mean by this perhaps? I guess, in one example, we were even running into situations where
runBlocking
inside, for example, a map's computeIfAbsent
was causing issues and I'm just not sure if there's a real way around that. Like, is there not a way you can, say, "detect" if you're in a coroutine and just run it anyway? I don't even know if that would make sense :')Adam Powell
09/05/2022, 4:55 PMpublic MyResult myFunction(MyParams params)
then when that returns, it has to actually return a MyResult
, not an IOU for one laterKieran Wallbanks
09/05/2022, 4:58 PMrunBlocking
where you don't know if you're in a coroutine or not, so, for the computeIfAbsent
example, we should just rework the system to not even need a suspending function?Adam Powell
09/05/2022, 5:01 PMMap<Key, Deferred<Value>>
instead of Map<Key, Value>
then computeIfAbsent
could be implemented using myScope.async { computeWithSuspendStuff() }
Joffrey
09/05/2022, 5:05 PMCould you expand on what you mean by this perhaps?Well for starters, here is one example: https://discuss.kotlinlang.org/t/why-invoking-join-method-on-a-coroutine-having-mainscope-as-scope-stopping-it/25464
runBlocking
, by its very definition, blocks the thread that calls it while its body executes (the lambda that you pass to it). Inside the body, you can then call suspending functions normally. This is usually fine because internally, runBlocking
uses the blocked thread to execute the body, creating an event loop out of it (but from outside of the body of runBlocking
, the thread is still blocked).
You might then try to switch dispatchers inside the body, like for instance withContext(<http://Dispatchers.IO|Dispatchers.IO>)
or something. In that case you're going outside the realm of that runBlocking
body, and if ever you need to run something on the original thread again, and wait for its result, you will face a deadlock.Adam Powell
09/05/2022, 5:16 PMrunBlocking
makes it really easy to create reentrant call situations in code that is almost certainly not equipped to be called in a reentrant wayKieran Wallbanks
09/05/2022, 5:36 PMrunBlocking
and doing something like scope.future { }.get()
?Joffrey
09/06/2022, 2:33 PM.get()
blocks the current thread just like runBlocking
, so this approach will have the same issues as runBlocking
if it tries to re-enter that thread within the dispatched task.