What's the "correct" pattern to call a suspending ...
# coroutines
d
What's the "correct" pattern to call a suspending library from an override of a non-suspending library method? My impl of
Copy code
abstract class TraversingPrimitivePreprocessor : Preprocessor {

  abstract fun handle(node: PrimitiveNode, context: DecoderContext): ConfigResult<Node>
needs to use the kotlin aws sdk to get the value. All of the kotlin aws sdk methods are appropriately suspending. Of course Claude spams
runBlocking
which I know how to use and to avoid if at all possible.
k
If
handle
isn't called from a coroutine, then
runBlocking
is fine. Otherwise, I'm actually not sure off the top of my head.
1
b
Suspending libraries (and even non-suspending components that initiate asynchronous work) can benefit from an "intimate coroutinescope" to manage their state and their coroutines. especially if you cannot decide in a vacuum/up-front, if this will be called from sensitive context (ui thread/main server looper) or will stay in background context. If you are in control of the caller library why not simply give its own scope to launch things in? It would become very robust.
k
He can't do that because
handle
returns a value which, presumably, he derives from some AWS data
👀 1
1
d
yeah, sorry. The abstract method is from hoplite configuration loading library not my own code. My code impls it but needs a coroutine scope in order to suspend.
1
b
you can leave it to the ultimate caller maybe to provide their scope
d
the abstract class is from
com.sksamuel.hoplite.preprocessor.TraversingPrimitivePreprocessor
the caller is the 3rd party library
well, i guess we call it and could start a coroutine and pass it as a thread variable (context)
k
if this is third party code that you know won't be called from a coroutine, then just use
runBlocking
.
💯 2
b
a context switch inside might not be ok. it would benefit you to change the method to add completion lambda or flow. but if you are locked in like this. i'd say runBlocking like Kevin suggests! i agree
d
ok, that's what i thought.
k
He can't change the signature since he doesn't define it.
1
b
not a lot you can do 😕
j
@Don Mitchell, why were you against using
runBlocking
initially? The documentation
runBlocking
says the following:
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.
Maybe I’m missing something but it looks like the best solution.
d
runBlocking
actually prevents coroutine process swapping (thus the name blocking) and when nested can cause process deadlocks. That is, if the coroutine pool is i and there are _i_/2 parallel parents each trying to create
i/2
chilldren each using
runBlocking
, you'll run out of pool after the first parent spawns all its children and the other ones won't be able to spawn theirs until the first finishes. The children won't take turns as they will block. (I'm fudging a bit based on having 2 conceptual models which clash on whether the blocking is per CPU or something else.)
k
runBlocking
should really be marked as
@DelicateCoroutinesApi
because of the deadlocking problem.
1
d
regretfully, at least my reading of @marcinmoskala’s coroutine book years ago led me to spam
runBlocking
across my code base. So, like an ex-smoker, I'm zealous about abolishing them now 😆
b
can i ask, is this intended to be used in ktor server environment? or maybe just a jvm process of a different nature, a cli process maybe?
m
Looks like a place for
runBlocking
, because: 1. You need result (if you don’t, it is typically better to use
scope.launch {}
) 2. You can block the caller (at least I assume so from how it looks)
runBlocking
is the proper way to call suspending calls from blocking functions.
k
ktor shouldn't really need to use runBlocking. It should be used sparingly in scenarios where you're bridging blocking code to coroutines code. The only two use cases I've found are: • Making a
main
function able to call suspending code. • Calling suspending code from a function you don't control like this thread talks about.
2
m
• Interoperability with legacy parts of our application that are still based on blocking calls
j
@kevin.cianfarini the
main
function can be marked
suspend
itself, this is no longer a valid
runBlocking
case (or at least not a case that requires
runBlocking
)
m
I just wrote an article about using runBlocking: https://kt.academy/article/run_blocking It is still a draft so it might change.
👀 1