https://kotlinlang.org logo
Title
t

trathschlag

03/11/2019, 12:45 PM
fun main() {
    runBlocking {
        val job = launch {
            suspendCoroutine<Unit> {}
        }

        yield()
        job.cancel()
        println("goodbye")
    }
    println("exit")
}
prints
goodbye
and then hangs forever. Does anybody know why this happens? When removing the
yield()
it just works as expected.
s

sitepodmatt

03/11/2019, 12:52 PM
fun main(argv: Array<String>) {

        runBlocking {
            val job = launch {
                suspendCoroutine<Unit> { continuation ->
                    continuation.resume(Unit)
                    
                }
            }

         yield()
            job.cancel()
            println("goodbye")
        }
        println("exit")


}
in your original example suspendCorountione wasn't created before the job was cancelled
s

streetsofboston

03/11/2019, 12:52 PM
Hmmm... All the runBlocking's child routines end (one by being cancelled) and runBlocking should too... What happens if you put a Thread.sleep(...) or delay(...) in the suspendedCoroutine (but still without calling 'resume')?
s

sitepodmatt

03/11/2019, 12:52 PM
because the dispatcher of runBlocking run everything on the same thread
when you added yield it was able to create it
but suspendCorotuine didnt finish because you didnt resume the continuation
r

ribesg

03/11/2019, 12:53 PM
@sitepodmatt note: you can use the item just below “Unread messages” on the top left of Slack to view threads in full width
🕺 1
🎉 1
s

sitepodmatt

03/11/2019, 12:53 PM
so the the runBlocking waits for a children to finish which it never would
t

trathschlag

03/11/2019, 12:55 PM
But the coroutine should be cancelled, even if it is suspended. My real usecase is more like this:
suspendCoroutine<Unit> { continuation ->
    performPotentiallyNeverEndingStuff(callback = {
        continuation.resume(Unit)
    }) 
}
Which effectively has the same problem
s

sitepodmatt

03/11/2019, 12:59 PM
you need a suspendCancellableCoroutine
un main(argv: Array<String>) {

        runBlocking {
            val job = launch {
                suspendCancellableCoroutine<Unit> { }
            }

            yield()
            job.cancel()
            println("goodbye")
        }
        println("exit")

}
💯 1
t

trathschlag

03/11/2019, 1:00 PM
Nice, this is what I was looking for. Thanks!
s

streetsofboston

03/11/2019, 1:04 PM
Still though; why did the original code then work when it has that 'yield()' call?
t

trathschlag

03/11/2019, 1:13 PM
It was the other way around: It did not work when the yield was invoked. And without the yield, the
runBlocking
body was never suspended, so the job did not even start executing before it was cancelled again. So the non-cancellable
suspendCoroutine
did not start and didn't block the cancellation of the job.
fun main(argv: Array<String>) {

    runBlocking {
        val job = launch(Dispatchers.Default) {
            suspendCoroutine<Unit> { }
        }
        job.cancel()
        println("goodbye")
    }
    println("exit")
}
This sometimes blocks and sometimes not, because if the default dispatcher manages to execute
suspendCoroutine
before the
runBlocking
finishes, the job is non-cancellable again.
s

streetsofboston

03/11/2019, 1:25 PM
That would explain it :) Still, I think it should not matter whether the dispatcher managed to execute 'suspendCoroutine' or not early on.... The 'launch' is called, it is not lazy, and the job should be considered either always running or always not running, not sometimes the one and sometimes the other....