Still after years of using coroutines, sometimes m...
# coroutines
m
Still after years of using coroutines, sometimes my brain suddenly questions itself. So probably a dumb question:
Copy code
withContext(<http://Dispatchers.IO|Dispatchers.IO>) { new { ... } }
I have DB code from Exposed (
new
entity creation) that is not suspending and has this parameter:
init: T.() -> Unit
, which as you can see does not allow suspension. If I now really need to call a suspended function, I would usually do so before the
new
block and reference the result. Now I thought, what happens if I just call
runBlocking
with my suspending code inside
new
? Will it “block more” this way, or be the same since the
new
block has no suspending capabilities anyway?
s
Yes, it’ll block “more”, because the call to
new
will take longer. So the thread it borrows from the IO dispatcher will be occupied for more total time.
m
Would you know any other workaround to call a suspended function inside the new block that would prevent this?
s
Depends on why you need to call the suspend function. But generally speaking the answer is going to be no. If you can’t suspend, your only option is to block.
Side effects while creating a DB entity seems like a bad idea anyway… although I haven’t used Exposed so I’m not familiar with the API
m
usually the suspending function isn’t making use that much of suspension anyway, for example it might just be a blocking DB call (for example to get/set a foreign key reference) that is wrapped inside a suspended block because of how my DAO api is structured. There is no delay or anything that would make real use of suspension normally
p
If
new
were
inline
you would be able to call a suspend fun inside its lambda if you already were in a suspend fun (as the lambda would be inlined)
s
Can
new
itself end up making a DB call? Because if yes then definitely don’t make another DB call inside of it 😬 that sounds like a recipe for connection pool starvation (with the caveat that I don’t know how Exposed works, maybe it’s actually fine)
m
I think the way Exposed works, you can nest “DB calls” since really its all wrapped in one transaction and executed at once after the transaction block finishes, but I will make sure no such problems would arise 😅
s
If it is indeed building a transaction for future execution then it shouldn’t need to block/suspend, right? blob thinking upside down I’m just confusing myself here 😄 carry on
m
Maybe I will rephrase: If I want to call a suspending function inside
new
that itself really is just a withContext(Dispatchers.IO) { // blocking code }, does it make it “block more” to use
runBlocking
? or would it only be a problem if the suspending function would actually make use of suspension in a proper way (then it makes sense to me that it would block instead of actually suspend)
@Patrick Steiger hm interesting idea, I don’t think I am able to have that type of influence of the API I call but I will keep it in mind
p
I think as Sam said, runBlocking may introduce a slight overhead that will make the thread block for a little longer assuming nothing will suspend inside runBlocking. Personally I have never used runBlocking in my life outside tests, and avoid it at all costs
s
Oh, okay, I understand what you mean. If there’s no suspension,
runBlocking
isn’t going to have much impact, but it will add a little bit of overhead for the event loop machinery it sets up. Aside from that overhead, it won’t block the thread any more than the code running within it already does.
There is potentially an optimisation that could delay creating the event loop until the first time the coroutine tries to suspend, but if they didn’t think it was worth doing that in the standard coroutines library, it’s probably not worth worrying about 🤞
m
Thank you 🙂
u
But isn't the "more blocking" ok as you are on dispatcher Io which is made for blocking?