https://kotlinlang.org logo
Title
u

uli

03/12/2021, 10:43 AM
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
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")
        }
    }
}
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

uli

03/12/2021, 10:47 AM
I stand corrected. will update
m

myanmarking

03/12/2021, 10:47 AM
1 has not completed and 2 has returned 😛 god this is a mystery indeed
u

uli

03/12/2021, 10:48 AM
That also explains why both run blocking are not waiting for the outer run blocking to complete
m

myanmarking

03/12/2021, 10:52 AM
moral of the story -> just respect Lint and do not use runBlocking in a context like that 😛
❤️ 2
l

louiscad

03/12/2021, 1:47 PM
There's no bug, it's just structured concurrency. runBlocking will block until all child coroutines are complete.
😅 1
🇳🇴 1
u

uli

03/12/2021, 1:49 PM
The second run blocking is not a child of the first. (Not counting the outer runBlocking)
l

louiscad

03/12/2021, 1:49 PM
@uli It blocks the
launch
, which is a child of the enclosing
runBlocking
.
u

uli

03/12/2021, 1:51 PM
@louiscad the first inner runBlocking is blocked by the second inner runBlocking
l

louiscad

03/12/2021, 1:51 PM
@uli Please, read my message again.
FYI,
runBlocking
behaves like
coroutineScope { … }
u

uli

03/12/2021, 1:52 PM
Look at the timestamp of "Done #1"
l

louiscad

03/12/2021, 1:54 PM
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

uli

03/12/2021, 1:58 PM
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

louiscad

03/12/2021, 1:59 PM
Couldn't this easily lead to deadlocks?
u

uli

03/12/2021, 1:59 PM
Sure.
But those would be expected
l

louiscad

03/12/2021, 2:00 PM
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

uli

03/12/2021, 2:01 PM
Actually we have a runSuspending here plus a bug
l

louiscad

03/12/2021, 2:01 PM
runSuspending?
u

uli

03/12/2021, 2:02 PM
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

louiscad

03/12/2021, 2:03 PM
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

uli

03/12/2021, 2:04 PM
And i argue the event loop should only be used inside the scope of this runBlocking
To really block other concurrent operations
l

louiscad

03/12/2021, 2:05 PM
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

uli

03/12/2021, 2:06 PM
Exactly. Sure not least surprise 🙂
Boils down to
then don't do it
But still a bug in my perspective
l

louiscad

03/12/2021, 2:08 PM
It's a feature, that looks like a bug when you do things you seem to agree should not be done.
m

myanmarking

03/12/2021, 2:11 PM
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

uli

03/12/2021, 2:33 PM
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

myanmarking

03/12/2021, 2:35 PM
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

uli

03/12/2021, 2:36 PM
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

myanmarking

03/12/2021, 2:40 PM
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