I just learned a lot about run blocking from this ...
# coroutines
u
I just learned a lot about run blocking from this thread: https://kotlinlang.slack.com/archives/C1CFAFJSK/p1615458029368600 Just for reference: 1. two
runBlocking
, launched on the same single threaded dispatcher do not block each other. They share an event queue. 2. former
runBlocking
will only return after later
runBLocking
completes (1) comes as a positive surprise (2) is totaly unexpected and I’d consider it a bug I’ll follow up in a thread with the demo code
👍 1
1
https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS4zLjQxIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncy[…]AgICAgbG9nKFwiRG9uZSAjMlwiKVxuICAgICAgICB9XG4gICAgfVxufSJ9
Copy code
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.Dispatchers

val start by lazy { System.currentTimeMillis() }

fun log(TAG: String) {
    val relTime = System.currentTimeMillis() - start
    println("$TAG @${relTime}ms in ${Thread.currentThread()}")
}

fun main() {
    runBlocking {
        launch {
            log("Start #1")
            runBlocking {
                log("Before delay #1")
                delay(100)
                log("After delay #1")
            }
            log("Done #1")
        }
        launch {
            log("Start #2")
            runBlocking {
                log("Before delay #2")
                delay(300)
                log("After delay #2")
            }
            log("Done #2")
        }
    }
}
Copy code
Start #1 @0ms in Thread[main @coroutine#2,5,main]
Start #2 @1ms in Thread[main @coroutine#3,5,main]
Before delay #1 @2ms in Thread[main @coroutine#4,5,main]
Before delay #2 @6ms in Thread[main @coroutine#5,5,main]
After delay #1 @105ms in Thread[main @coroutine#4,5,main]
After delay #2 @307ms in Thread[main @coroutine#5,5,main]
Done #2 @307ms in Thread[main @coroutine#3,5,main]
Done #1 @307ms in Thread[main @coroutine#2,5,main]
1. both of these 
runBlocking
 will only return after both are completed -> seems not to be it
u
I stand corrected. will update
m
1 has not completed and 2 has returned 😛 god this is a mystery indeed
u
That also explains why both run blocking are not waiting for the outer run blocking to complete
m
moral of the story -> just respect Lint and do not use runBlocking in a context like that 😛
❤️ 2
l
There's no bug, it's just structured concurrency. runBlocking will block until all child coroutines are complete.
😅 1
🚫 1
u
The second run blocking is not a child of the first. (Not counting the outer runBlocking)
l
@uli It blocks the
launch
, which is a child of the enclosing
runBlocking
.
u
@louiscad the first inner runBlocking is blocked by the second inner runBlocking
l
@uli Please, read my message again.
FYI,
runBlocking
behaves like
coroutineScope { … }
u
Look at the timestamp of "Done #1"
l
Aaaah, didn't click the "Show more", so I was looking only at part of the snippet 🤦‍♂️
🙂 1
You can "fix" it by passing
<http://Dispatchers.IO|Dispatchers.IO>
to the enclosing
runBlocking { … }
. Doing blocking "concurrency" with a single thread will always not work as expected.
The problem is misuse of the APIs regarding the use case here. There's nothing kotlinx.coroutines could do better without breaking other correct code.
u
Yes. Agreed. I'd still find it less surprising if runBlocking would simply block the thread with a new run loop to avoid any concurrency. And then return as soon as it is done
After all it's called run blocking
l
Couldn't this easily lead to deadlocks?
u
Sure.
But those would be expected
l
Not necessarly if the implementation details are hidden I believe blob thinking fast
Maybe there could be an experimental API to disable this behavior?
u
Actually we have a runSuspending here plus a bug
l
runSuspending?
u
Yes. You are on main thread and call run blocking. After that someone else can dispatch on that main thread
So the thread is not blocked. It is suspended
The two runBlocking should not run concurrently if it were blocking
l
Thread is "blocked", by an event loop, making it available for stuff instead of being blocked needlessly (as would happen if you pass a different dispatcher)
u
And i argue the event loop should only be used inside the scope of this runBlocking
To really block other concurrent operations
l
It kinda does already, just that it crosses the boundaries of other
runBlocking
calls happening on the blocked thread.
If your application depends on this behavior, it's probably tied too much to implementation details and low-level concurrency stuff.
u
Exactly. Sure not least surprise 🙂
Boils down to
then don't do it
But still a bug in my perspective
l
It's a feature, that looks like a bug when you do things you seem to agree should not be done.
m
there is no context of blocking in coroutines. It is only called runBlocking because it is a bridge from coroutines to normal non-coroutine code. even if you use runBlocking inside a suspend coroutine, it won’t block anything
blob thinking fast 2
u
there is. runBlocking is blocking the calling runLoop. Unless the runloop is one created by runBlocking on the same thread. then it is reused. check this one. If I replace the outer runBlocking by a regular single threadded dispatcher all concurrency is gone: https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS4zLjQxIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncy[…]luZ1xuICAgIHJ1bkJsb2NraW5nIHsgbWFpbkpvYi5qb2luKCkgfVxufSJ9
m
no there isn’t. It is only ‘blocking’ because it is used improperly. There is no blocking concept in coroutines, because it doesn’t make sense
u
That is, because the outer run loop is blocked and can not proceed to the second inner launch. This is expected. Only that it behaves differently if the outer runLoop is also a ‘’runBlocking’-Loop is surprising.
m
It doesn’t matter. The api was not designed to be used that way. If you do not use it as it was supposed to, then sure you find this kind of ‘bugs’
would you say java has a bug concerning this one? It doesn’t matter whether it’s a bug or not. The user of the API is clearly breaking its rules