Hey. When I use `cancel()` like here: ```@Test fun...
# coroutines
a
Hey. When I use
cancel()
like here:
Copy code
@Test
fun myTest() = runTest {
    launch {
        cancel()
    }
}
then what is being cancelled - the launched job or the `runTest`'s scope? I did some testing and I used a custom
scope: CoroutineScope
Copy code
val job = scope.launch {
    // Uncomment one of these to compare behaviour:
    cancel() // from: import kotlinx.coroutines.cancel
    //coroutineContext.job.cancel()
    //scope.cancel()
}
and it turned out only
scope.cancel()
cancelled the whole
scope
, while all the other statements only cancelled the
job
which is weird because
cancel()
imported by
import kotlinx.coroutines.cancel
is:
Copy code
/**
 * Cancels this scope, including its job and all its children with an optional cancellation [cause].
 * A cause can be used to specify an error message or to provide other details on
 * a cancellation reason for debugging purposes.
 * Throws [IllegalStateException] if the scope does not have a job in it.
 */
public fun CoroutineScope.cancel(cause: CancellationException? = null) {
    val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
    job.cancel(cause)
}
so, according to the documentation, it is supposed to cancel the scope, not the job. Does anyone know if this is some kind of bug?
❤️ 1
K 1
j
What you may have missed is that
launch
provides a
CoroutineScope
as receiver to the lambda. That's the one you're cancelling
a
I'm aware of that. What is that scope? Is this the same scope where
launch
was invoked (i.e.
CoroutineScope.launch()
)?
so when I
Copy code
scope.launch {
    cancel()
}
Should I cancel
scope
?
If yes, then something is clearly wrong, at least in the test code I wrote: https://pl.kotl.in/8lRGj7vXt
j
No it's a child scope, which
launch
creates with a job that has the parent scope's job as parent
a
child scope? So
launch
creates a new (child) scope?
m
I believe its a child context not scope. There are many things with a cancel function
a
I agree with @mkrussel. The documentation doesn't mention any "child scope" when it explains what
launch
does
m
Also why you cannot just launch inside a launch
a
exactly ah, nope, you actually can
j
@mkrussel the instance provided is of type
CoroutineScope
, and you can definitely launch inside launch. That's the whole basis of structured concurrency
a
I thought you need to use
coroutineScope { ... }
to create a child scope, no?
j
That's one way, but any coroutine builder also creates a scope.
withContext
also creates a child scope
m
It is a scope and not a context
I was wrong
The accepted answer says:
it's a sibling of the outer launch since they were both launched from the same scope
OK, I'm stupid. It later says the outer launch created a scope.
j
it says in the first snippet, which explicitly launches the inner launch in the same explicit
scope
variable. The second example uses the child scope of the outer
launch
as implicit receiver, which is the child scope I'm talking about here.
a
Thanks for help guys. Since the documentation doesn't mention
launch
creating a new scope, I thought something must be wrong in the test code I wrote.
j
the doc is not crystal clear indeed
a
To be honest, IMO this should be clearly stated in
launch
KDoc. Otherwise, it's hard to guess what is the lambda's receiver exactly.
Thanks again. You saved me from a sleepless night 😄
j
Btw, here is a nice blog post mentioning those child scopes if you're interested: https://elizarov.medium.com/coroutine-context-and-scope-c8b255d59055