-
Notifications
You must be signed in to change notification settings - Fork 1.9k
runBlocking
can cause other coroutines to be dispatched on the calling Thread which should be blocked?
#3204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Discussion in kotlinlang slack here: https://kotlinlang.slack.com/archives/C1CFAFJSK/p1645717325416749 |
Another way of asking this question with the underlying code would be to say why does
Since it processes the next event on that
My understanding would have been that we would only CC @elizarov are just supposed to consider |
Another, simpler way to demonstrate the unexpected behaviour:
Prints: A. Start Thread[Test worker @coroutine#1,5,main] No expectation here that B should be dispatched before E. |
And a simpler reproduction of the original case:
Still dispatches B and C even though it should be blocked after D. |
Thinking about this more .... runBlocking is supposed to block the calling thread but to do so it tries to use the EventLoop of the calling thread to run other coroutines queued for dispatch on the thread. Presumably it needs to use the EventLoop of the dispatcher it runs on to keep processing events - any coroutines waiting for dispatch - within the scope of the runBlocking lambda. But that this would include other coroutines on the same thread (i.e. that we would pull in the ThreadLocal EventLoop) is unexpected behavior; at least to me! If you are blocking the thread why run other coroutines waiting to run on that thread that are not within what you specified to be run in runBlocking? Shouldn’t we have a sub-scoped EventLoop strictly for anything launch within runBlocking? @qwwdfsad am I understanding this correctly? |
This is not the bug, this is our intentional design and it's reflected in our documentation (though it probably might be improved):
|
Yes this makes sense to me and is necessary - my issue is that it also progresses other coroutines dispatched outside of the |
Indeed. We've added it as part of #860 The core problem with Meaning that when you have more than one library that use |
Actually I was expected I was wrong. It's as @qwwdfsad said:
With my emphasis on THIS runBlocking loop. So in this example
The first coroutine with Can someone confirm this is correct (and thus that my interpretation of @qwwdfsad is correct). I know that's what the code does, but would like confirmation that is the intended behavior. |
Thanks @qwwdfsad that helps me understand and makes a lot of sense. So how would you suggest calling a suspending API in a blocking way that wouldn't co-operate with the outer event loop? Example use case: Your process is crashing and you want one suspend API to be called but not to dispatch any other coroutines on the main thread. |
@arberg I don't think this is true - this is exactly what we are talking about. (This won't work in a unit test unless you |
@steve-the-edwards What I'm talking about is a slightly different variant of the initial question, though it was unclear to me whether that was part of your question. Actually I'm not sure what you mean by first scope can be dispatched for A. Yes, first scope could run first, and would if using Main.immediate, or start=UNDISPATCHED, but (apparently?) won't as I wrote it. |
@arberg At least I understand it this way and think it is correct. We are on the main thread launching a coroutine to be dispatched later on the same main thread and before this coroutine gets a chance to be dispatched we are blocking said main thread with runBlocking. If we replace runBlocking with a call to a normal function doing the printing the result will be the same. Why should runBlocking be different and somehow be aware of what we are doing outside of it? |
@pacher
I agree that is what happens.
Yes I agree that is a reasonable way to look at it. The difference though being that runBlocking is actually a coroutine that runs other coroutines and thus allows running any 'other' (child) coroutines. That includes everything that is a coroutine such as executing sub-coroutines on other threads (such as withContext), so I thought it would also allow other coroutines dispatched on same thread to run. But it won't, and I agree with you it makes total sense in that its 'Blocking'. I really shouldn't have been that surprised... but I was because I thought of it as a coroutine loop-handler, which it is, it just only handles it own children. |
@steve-the-edwards I suggest you try the code I included, and wonder why the |
No idea honestly, never give it a proper thought. The name should clearly reflect that is not intended to be used on libraries (note: the |
What about using |
The kdoc for
runBlocking
claims it blocks the thread from which it is called (interuptibly). But I have observed behaviour where other coroutines waiting for dispatch on that thread (e.g. on the main thread) will be dispatched by the event loop becauserunBlocking
falls back to using the calling thread's eventLoop even when a specific other dispatcher (which may be busy) is specified for that which to run the coroutine launched byrunBlocking
on. Why is this and is this a bug? If not what is the explanation for this choice of behaviour?E.g. This code can reproduce the issue:
This prints: A, D, B ,C then hangs. I would have thought the coroutine for B and C would never have gotten dispatched but runBlocking picks up the calling threads eventLoop to processNextEvent while waiting?? It should be blocked though?
I realize
runBlocking
has a note about not using it from within a coroutine - but I'm not sure it's possible to know that as you could be running on any coroutine but not within a suspend function and you wouldn't know your execution context, as any coroutine could call any non-suspend function.Can we clarify the documentation here or enforce the constraints?
runBlocking
has useful utility when calling coroutine based APIs from a blocking API.The text was updated successfully, but these errors were encountered: