I came across some unexpected behavior while using coroutines in Android app.
Suppose I've got the following function, which is not "suspend". It starts worker threads and should block the calling thread until all workers terminate:
fun doSomething() : Result {
// producers init thread
Thread {
for (i in 0 until NUM_OF_MESSAGES) {
startNewProducer(i) // each producer is a thread
}
}.start()
// consumers init thread
Thread {
for (i in 0 until NUM_OF_MESSAGES) {
startNewConsumer() // each consumer is a thread
}
}.start()
synchronized(lock) {
while (numOfFinishedConsumers < NUM_OF_MESSAGES) {
try {
(lock as java.lang.Object).wait()
} catch (e: InterruptedException) {
return@synchronized
}
}
}
synchronized(lock) {
return Result(
System.currentTimeMillis() - startTimestamp,
numOfReceivedMessages
)
}
}
I know that (lock as java.lang.Object).wait() is ugly and I'd be better off with ReentrantLock, but I intentionally wanted to fall to the most primitive level.
Now, if I execute this function without coroutine from Android's main thread, it blocks the calling thread (expected behavior):
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
someObject.doSomething()
}
However, if I just wrap it in a coroutine which is also executed on main thread, main thread isn't blocked anymore, but the functionality remains the same:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CoroutineScope(Dispatchers.Main).launch {
val result = someObject.doSomething()
}
}
Two questions:
- I thought that in order for coroutines to work, the functions should be "suspend", but it's not the case here. So, what's the point of "suspend" then?
- Call to
(lock as java.lang.Object).wait()should've blocked the main thread. How comes it doesn't when coroutine is involved? Do coroutines have a mean of "intercepting" such low level interactions?
Thanks