Hey all! Trying to understand how cancellation wor...
# coroutines
h
Hey all! Trying to understand how cancellation work with
coroutineScope
by launching a coroutine inside
coroutineScope
and then cancelling it. I was expecting the child coroutine to cancel but seems it continue to run even after being cancelled
Copy code
fun main() = runBlocking {
    val job = coroutineScope {
        launch {
            delay(1000)
            println("Is the coroutine cancelled? $isActive")
            println("From coroutine inside coroutine scope")
        }
    }
    job.cancel()
}
Here's the code that I tried
And it outputs:
Copy code
Is the coroutine cancelled? true
From coroutine inside coroutine scope
j
coroutineScope
is a suspending function that suspends until all child coroutines complete. This means the line
job.cancel()
cannot be reached here until the child coroutine is actually done. It would work if it were reached
h
I keep missing this fact but now it all makes sense 🙂 Thank you!
coroutineScope
itself is not a coroutine right?
j
No it's not a coroutine, it's a simple suspending function, but it allows to launch coroutines. It is useful because it provides a child scope that inherits the current context so it allows to encapsulate an area of code where you can run several coroutines concurrently (e.g. to parallelize some work) and then wait for everything to complete and return a result. All of this without the need to provide some external scope to run your child coroutines.
If you want to test cancellation in the case of
coroutineScope
usage, here is an example that uses
coroutineScope
in a more common way: https://pl.kotl.in/3vVQzIJq2
👌 1
h
All of this without the need to provide some external scope to run your child coroutines
Not sure what external scope means here and how
coroutineScope
helps here
j
Sorry for being unclear. By "no need for external scope" I meant that the function launching the coroutines doesn't need to be given a
CoroutineScope
from somewhere. Using
coroutineScope
, it gets an instance of
CoroutineScope
, which it can use to launch coroutines.
In general you can group functions that launch coroutines into 2 categories: • functions that launch coroutines and return before the completion of those coroutines • functions that launch coroutines and return after all coroutines have completed Functions from the first group need some
CoroutineScope
to launch the coroutines in, because we need to have some sort of lifetime for those coroutines so they can be cancelled properly. These functions usually shouldn't be marked
suspend
, and by convention they are often declared as extensions on
CoroutineScope
. They have this kind of signature:
Copy code
fun CoroutineScope.doSomethingThatLaunchesCoroutines() { ... }
Functions from the second group can use
withContext
or
coroutineScope
to scope their coroutines, because they will not return (resume) before the coroutines are done. This means they will automatically cancel their child coroutines if the coroutine in which the function is called is cancelled, so there is no leak, and yet no need to get a scope as argument or receiver.
h
So
withContext
and
coroutineScope
facilitates coroutine launch and make sure that we don't proceed until all the child coroutines from these scopes are finished. But
launch
also provides
CoroutineScope
as implicit receiver on its
block
argument meaning it also facilitates launching coroutines. And in this case, I think it will launch coroutines and will just return not waiting for child coroutines to complete. Will this also cancel all of the child coroutines if parent
launch
is cancelled?
j
So 
withContext
 and 
coroutineScope
 facilitates coroutine launch and make sure that we don't proceed until all the child coroutines from these scopes are finished.
Exactly.
launch
 also provides 
CoroutineScope
 as implicit receiver on its 
block
 argument meaning it also facilitates launching coroutines. And in this case, I think it will launch coroutines and will just return not waiting for child coroutines to complete.
The coroutine started by 
launch
 will wait for its child coroutines to finish before completing. But the call to 
launch
 will indeed return immediately.
Will this also cancel all of the child coroutines if parent 
launch
 is cancelled?
Yes (at least in general, unless you override the Job in the coroutine context of child coroutines)
h
And does child coroutines of
launch
execute concurrently?
j
Several child coroutines inside a
launch
are run concurrently with each other, and concurrently with the rest of the body of the
launch
:
Copy code
launch { // parent launch

    launch { // child launch 1
        // concurrent with the code that follows in the parent launch
    }

    // code here is concurrent with child launch 1

    launch { // child launch 2
        // concurrent with child launch 1 if not already completed
    }

    // code here is concurrent with child launch 1 and 2
}
h
This makes things much more clearer. Thank you so much @Joffrey 👍
☺️ 1