Can somebody explain why the coroutine context of ...
# coroutines
j
Can somebody explain why the coroutine context of a
suspend fun main
is considered inactive?
Copy code
suspend fun main() {
    println(currentCoroutineContext().isActive) // prints false
}
https://pl.kotl.in/sVFnLz9mM
e
suspend fun main
uses a super simple dispatcher instead of depending on kotlinx.coroutines. since it doesn't have a
Job
, it's
!isActive
. https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/jvm/src/kotlin/coroutines/jvm/internal/RunSuspend.kt
this
Copy code
suspend fun main() = coroutineScpoe {
    println(isActive)
}
produces
true
because it goes into the kotlinx.coroutines world, but without that, it doesn't
j
Oh that makes sense now, thanks. For some reason I didn't realize
coroutineContext.isActive
was from
kotlinx.coroutines
, unlike
coroutineContext
. Yeah I understand the other options of using
coroutineScope
or removing
suspend
and using
runBlocking
. That said, it makes using
suspend fun main
pretty awkward if you use things that are aware of structured concurrency without wrapping them in coroutines first. I wonder if production code should use it at all, now.
e
it's fine, the only way you can use structured concurrency is by using kotlinx.coroutines primitives to get a CoroutineScope first
j
I just hit a bug in production code (second time actually) that was calling a
suspend fun
from a library that relies on
while(coroutineContext.isActive)
. I didn't need to launch a coroutine to call that function from
suspend fun main
.
Now I'm wondering whether the incorrect pattern is the fact that I was calling a
suspend fun
directly from
suspend fun main
without coroutine, or the fact that the lib function was using
while(isActive)
🤔
e
while (currentCoroutineContext().isActive)
seems like a bug to begin with. it'll fail in anything else that uses (non-kotlinx.)coroutines too, e.g.
sequence()
builders
j
AFAIR
sequence
builders have restricted
suspend
access via special annotations, though, exactly to avoid this kind of problems
e
hmm, that's true. I still think whatever function contains that code would be better off using a scope explicitly
j
I don't believe a scope is appropriate in this case, though. IMO if the suspend function doesn't launch coroutines that outlive its own scope, it shouldn't require an external scope. The reason for the
while(isActive)
in the function in question is just superstition I believe, because the loop contains suspending calls anyway so it shouldn't be necessary. And even if that were not the case, we could also introduce
yield()
calls in the loop and it would have the same effect of detecting cancellation. Still begs the question, should
while(isActive)
be considered an anti-pattern in general?
e
I think
while (isActive)
could be useful if you're looping around non-suspending code, but it should be known to come from a
CoroutineScope
- from a function local
coroutineScope {}
if it's not given an external scope. a
CoroutineContext
is usually not something you'd be touching
if you're suspending (or using
yield()
to suspend) then it's probably not useful to use
isActive
, I think
j
I think the problem is that
isActive
is defined on the
coroutineContext
itself, even though it doesn't have any meaning when there is no
Job
in the context. I think defining it at this level anyway with the value
false
is just a footgun.
Even if providing a local scope with
coroutineScope
would work, it is painfully not obvious why it would be there if no coroutines are launched in it. The connection between the presence of
coroutineScope
and the usage of
coroutineContext.isActive
is not shown by the compiler, and I think seeing such code I would be the first person to carelessly remove the "useless"
coroutineScope
wrapper
e
it's also defined on
CoroutineScope
, but I agree the one on
CoroutineContext
is a likely footgun
j
Ah in that case I take back my last message. Using the one defined on
CoroutineScope
would solve the problem I was mentioning
e
yep, taking out
coroutineScope
from
coroutineScope { while (isActive) { ... } }
will break compilation
👍 1
a
If I run the same playground link from the Kotlin version
1.9.25
it starts to print
true
(before
1.9.25
it is still giving false.) Is there any change to this behaviour?
j
Yes, I had reported the old behavior as a problem after this conversation, and an issue was created and fixed eventually: https://github.com/Kotlin/kotlinx.coroutines/issues/3300
👍 1