Hello guys. I am struggling with `runBlocking`. Th...
# coroutines
t
Hello guys. I am struggling with
runBlocking
. The documentation says that it should be used in
main
, tests and libraries to bridge two worlds. I found zero information about structured concurrency within plain command line applications. Lets say I have a tool which migrates data from one source to another and I would like to use concurrent executions for some parts of the whole migration process. Now, should I define
runBlocking
somewhere "in the middle", or how to use coroutines in the world where no life cycle managers exists? Thanks
n
runBlocking
is your lifecycle. It gives you a
CoroutineScope
as the lambda receiver to represent the lifetime of the
runBlocking
call.
runBlocking
won't return until all coroutines launched with that
CoroutineScope
have finished. For example, if you were to just call
GlobalScope.launch
then main could return and end the process before the coroutine had finished.
j
It's totally fine to use
runBlocking
"somewhere in the middle"
šŸ‘€ 1
n
Where ever you use
runBlocking
, just avoid calling it from within a coroutine. If
runBlocking
depends on code waiting to run on threads blocked by
runBlocking
you get a deadlock.
t
@Nick Allen That's all I have been reading in past few days. But the documentation about runBlocking usage puzzled me. @Joffrey That's what I will end up with I guess. But still thinking about other options..
j
Yes of course don't use
runBlocking
from coroutines/suspend functions. But otherwise if the goal is to synchronously call a suspend function from the non-suspending world, it's what
runBlocking
is for. And in the context of CLI applications, this is often the case. Usually though, you can bubble up the
suspend
keyword almost all the way up to the
main
method (but you don't have to)
n
Use
runBlocking
anytime you want to hold up a thread until you are done with some coroutine work. So
main
is a good example. Also, synchronous callback APIs where the purpose is to allow you to do side effects, rather that to give you a result. Or if you need to implement a synchronous interface or override a synchronous method. If your caller must be synchronous but you want to use coroutines, that's where you need to use
runBlocking
.
t
Thank you both! I will give it a try.
k
I don't think it's a good practice to call
runBlocking
half way down the pipeline. It would be easy to have a long waited suspend function easily misplaced into the
runBlocking
and have a unwanted delay in your code execution. For example, at my work we use internal authentication library that uses
runBlocking
to add authentication headers and it's causing every request to have a longer timeout then we want inside of the app, because
runBlocking
had code inside that was blocking the thread. So in general, I would avoid it.
t
Well, than what is you recommendation? How should one deal with coroutines in the middle of the CLI app?
j
@K Merle a library is a different animal. It's a matter of deciding whether to expose a blocking API, a suspending API, or a Future-based API. If the lib uses coroutines and expects to be used only by coroutine-based consumers, it's better to expose a suspending API. However, if you chose a blocking API for your library, you simply don't have a choice inside the lib, like in any place where the caller is blocking and expects a result synchronously. So IMO the problem is to have chosen a blocking API in the first place. Why do you think the problem in your lib is
runBlocking
? If the library takes a long time to compute those headers, and exposes a blocking API for it that guarantees the work is done when it returns, you'll have to wait and block the caller thread anyway. It could be using something entirely different, not even coroutines, and the result would be the same.
k
@tomas-mrkvička I don't have any suggestions beside that one. Could possibly execute it not in the middle. @Joffrey Problem was the underlying API (library) that generates headers and was executed in
runBlocking
, not the
runBlocking
itself. It was misused.
j
So your application was calling an internal authentication library that was exposing a blocking API, using
runBlocking
inside, and calling a third-party library within
runBlocking
. Is this correct? Was that third-party library exposing a blocking API?
k
Internal library was having a connection timeout, and if there was no network connection it would hold a network request for couple of seconds, and we needed an error right away.
l
Note that there are good ones to bridge from suspending to blocking functions, like
<http://Dispatchers.IO|Dispatchers.IO>
and
runInterruptible
.
runInterruptible
helps translating cancellation calls to Thread interrupt which will work for traditional java IO.
k
Joffrey, in your message above,
you can bubble up theĀ 
suspend
Ā keyword almost all the way up to theĀ 
main
Ā method
can you explain why you said "almost"?
j
This was me being cautious, but using both "usually" and "almost" in the same sentence is unnecessary šŸ™‚ If you have full control, you can of course even make the
main
function itself
suspend
. I was cautious because if you use some framework, maybe you don't have access to
main
, or
main
may just call the framework (which you don't control), which in turn calls you through interfaces or classes in a blocking way. When in this situation, your "entrypoint" because something else than
main
and so you may not be able to make that one
suspend
(let alone go all the way up to
main
with
suspend
functions) - but you can use
runBlocking
there, that's what it's for
āœ”ļø 1