I found this snippet of code for an interesting case of an infinite loop (it's not a deadlock as one thread/coroutine keeps progressing) with Kotlin coroutines, but I can't seem to wrap my head around why removing the first yield() solves the issue?
Tried debugging it and it seems that while (job.isActive) yield() just schedules itself to run over and over, instead of scheduling job coroutine which I would be expecting.
Can someone explain why this code doesn't work, and why removing the first yield()fixes it?
#Test
fun testDeadlock() = runBlocking {
val job = launch {
runBlocking {
println("1")
}
println("2")
}
yield() // Comment yield and test starts to pass
runBlocking {
println("3")
while (job.isActive) yield()
}
}
Related
I'm studying coroutine,
and writing some examples, I found some weird things.
even if I write the launch build first,
it start late than coroutineScope.
I understand coroutineScope has a suspending point.
Is there any one who explain this?
below is my code
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("Start")
}
coroutineScope {
launch {
delay(1000)
println("World!")
}
println("Hello")
}
println("Done")
/*
expected
Start
Hello
World!
Done
result
Hello
Start
World!
Done
*/
}
launch block isn't executed immediately. It is sort of scheduled for asynchronous execution and the function returns immediately. That's why "Start" is not printed immediately and the control moves inside the coroutineScope. You can verify this with a simple example like:
launch {
println("Start")
}
println("End")
Here you will see that End is printed before Start
Consider this non cancellable coroutine that works as its name implies.
fun main(args: Array<String>) = runBlocking {
val nonCancellableJob = launch(Dispatchers.Default) {
for (i in 1..1000) {
if (i % 100 == 0) {
println("Non cancellable iteration $i")
}
}
}
println("Cancelling non cancellable job...")
nonCancellableJob.cancelAndJoin()
}
Now, if I get rid of the explicit dispatcher Dispatchers.Default and use the inherited one i.e. launch {...} the coroutine gets cancelled immediately without printing anything. It seems that a non cancelling coroutine is being cancelled! Is it a bug or what?
When you don't use Dispatchers.Default then launch is inheriting dispatcher of runBlocking which seems to defer execution until needed.
Even though your coroutine cannot be canceled while running (since it doesn't have any activity check nor does it have suspension points) it's still possible to cancel it before it starts executing.
You can modify CoroutineStart of your launch builder to alter this behavior:
To execute it immediately when declared:
val nonCancellableJob = launch(start = CoroutineStart.UNDISPATCHED) { ... }
To prevent cancellation before it starts:
val nonCancellableJob = launch(start = CoroutineStart.ATOMIC) { ... }
The inherited dispatcher runs on the same thread where you launched it. This works such that runBlocking establishes a top-level loop that goes over the coroutines running inside it, resuming them one by one. It can't resume the next coroutine before the current one has suspended itself.
In your code, the top-level runBlocking coroutine created one child coroutine and then, without suspending, went on to do some more things: print a message and cancel the coroutine it just created. At this point the coroutine is still hanging in the limbo of having been created but not run. Your cancelAndJoin call cancels it in this state, before it has got its chance to hog the runBlocking dispatcher forever. It immediately changes the state to "completed through cancellation" and the whole program ends.
I'm new to Kotlin coroutines topic and there is one issue, which totally blocks me from using them. I have the following code to run with coroutines:
runBlocking {
for (i in 0 until args[1].toInt()) {
GlobalScope.launch {
OuterObject().run()
}
}
And my OuterObject class has the following code in run() method:
override fun run() {
...
logger.info(){ "Checkpoint 0" }
var innerObject: InnerObject = InnerObject(some_parameter = 1)
logger.info(){ "Checkpoint 1" }
...
}
All the coroutines got started from the loop but reach only "Checkpoint 0". There are no log messages or any other actions after innerObject creation attempt.
The first thing I tried is to create another object, but it seems like the issue is general and does not depend on object's class. I've tried with DateTime(), Gson() and some others - each time coroutines stop at this point. I've also tried to add exception handled to the coroutine but there is no exception catched - the coroutine just silently stops.
What is the reason behind this and how can I avoid it?
Version of kotlinx-coroutines-core: 1.2.2
UPDATE 1:
I've checked a primitive type assignment and it works. Both "Checkpoint 0" and "Checkpoint 1" appear in console logs. The issue is only with complex types.
override fun run() {
...
logger.info(){ "Checkpoint 0" }
var test = 1
logger.info(){ "Checkpoint 1" }
...
}
UPDATE 2:
#gladed and #Sam, you are right. The article of #roman-elizarov also helped me out: The reason to avoid GlobalScope. It was wrong to call "launch" from GlobalScope - it is not related to the runBlocking scope.
I've modified my code in the following way:
runBlocking {(0 until args[1].toInt()).forEach {
launch(Dispatchers.Default) {
OuterObject().run()
}
}
I got some further issues with running the code this way but they were due to not optimal concurrency.
launch() starts a new coroutine. But you're not waiting for it to complete. Instead, consider:
runBlocking {
(0 until args[1].toInt()).forEach {
launch {
OuterObject().run()
}
}
}
This way, runBlocking won't return until all the launched jobs are complete. (Credit to #Sam on removing GlobalScope.)
In Vert.x, suppose I have functions like this:
fun caller() {
runBlocking {
val job = GlobalScope.launch(vertx.dispatcher()) {
val r = suspendPart()
println(r) // never execute
}
println(1) // printed
job.join()
println(2) // never execute
}
}
suspend fun asyncPart(): Future<Int> {
val promise: Promise<Int> = Promise.promise()
delay(500)
promise.complete(0)
return promise.future()
}
suspend fun suspendPart(): Int {
return asyncPart().await()
}
r(which is 0) and 2 will never be printed, only 1 is printed. How should I fix it?
My intention is to wait for asyncPart completes (I have a AsyncResult inside actually).
Presumably your caller() method is called by vert.x and this means you're breaking one of the pivotal rules of vert.x:
Don’t block me!
Vert.x is mostly based on very fast single-threaded work, what this means is that when you block the thread in caller, it is unable to execute the coroutine scheduled with launch leading to a deadlock.
The proper way to solve this is to remove your blocking code through the integration vert.x provides for kotlin coroutines.
Alternatively using a different dispatcher for launch would also work since the other thread would unblock the vert.x dispatcher. But this would not solve the primary issue of blocking calls in the vert.x dispatcher.
I started to learn coroutine and have tried run the code from example structured-concurrency. But I got another result. If set delay(1000L) "Hellow, " only was printed and Process finished with exit code -1073741819 (0xC0000005). But if I set delay(100L) I get "Hello,World!". Why launch{} block not launch?
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
launch { // launch a new coroutine in the scope of runBlocking
delay(100L)
println("World!")
}
println("Hello,")
}
screen 1
screen 2
Okey, I disabled the program "Punto switcher" and now all work correctly.