I'm using a scope to check if some parts of the co...
# coroutines
r
I'm using a scope to check if some parts of the code should be active or not, is this the correct way to go about it?
Copy code
when (cardMessage) {
            is CardMessage.TagLost -> {
                cardScope.cancel("Card left field")
            }
            else -> {
                cardScope = CoroutineScope(Dispatchers.Default)
            }
        }
u
The cancel is fine. Just wondering, if you intend to create a new scope for each new card message that is not
CardMessage.TagLost
r
Pretty much. You get a
TagLost
after each card message.
I also considered
SupervisorJob
, would that also work?
u
Oh, I think you need to add some kind of Job. Wether it is supervisor or not depends on the children you spawn. if you spwan multiple children, what do you want to do if one fails? Fail all? Then use SupervisorJob
r
So instead of a
cardScope
, better use a
cardJob
and use
SupervisorJob
to create that.
u
I was thinking more like this:
Copy code
cardScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
r
Also an option 🤔
u
Alternatively you could initialize the scope once:
Copy code
cardScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
And then go like this:
Copy code
when (cardMessage) {
            is CardMessage.TagLost -> {
                cardJob.cancel("Card left field")
            }
            else -> {
                cardJob = cardScope.launch() { /* Do some work */ }
            }
        }
r
I assumed
cardJob = SupervisorJob()
u
Maybe forget about the card job, it breaks structured concurrency.
r
Ok.
So
cardJob = SupervisorJob()
and then cancel the
cardJob
, so it'll stop all coroutines inside the cards?
u
I do not really understand. how do you want to conenct the coroutines to the card job?
r
CoroutineScope(Dispatchers.Default + cardJob)
Or maybe I didn't fully grasp the "breaks structural concurrency" part
u
oh, that’s equivalent. scope.cancel call scope.job.cancel (pseudocode)
r
... I assumed it meant "the cancel's gonna break if one of the started coroutines misbehaves"
u
Copy code
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)
}
And you also can not re-use the cardJob as it is canceled. YOu need to create a new one for each new scope
r
I can recreate it though, when I make a new
SupervisorJob()
?
u
yes. but then you get the equivalent of just canceling the scope
see the code above, copied from the CoroutineScope implementation
no need to hold on to the job object. the scope will hold it for you and use it to cancel the children.
r
a) I wanna cancel everything on the
cardScope
b) I want a coroutine being cancelled not kill everything on
cardScope
To achieve that, use
cardScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
?
u
to kill a single coroutine you can do as i posted above (and then withdrew 🙂 ) Just save the job object that is returned when launching the coroutine and cancel that job object. your scope should then have a supervisor job so it won’t be canceled by it’s child failing.
r
How would I use that scope downstream?
u
Copy code
when (cardMessage) {
            is CardMessage.TagLost -> {
                cardJob.cancel("Card left field")
            }
            else -> {
                cardJob = cardScope.launch() { /* Do some work */ }
            }
        }
Here you only cancel the job of that one coroutine. cardScope will be unaffected and can be reused for the next coroutine
> How would I use that scope downstream? depends on your overall architecture. I have no idea where and how you create your coroutines
r
I'd prefer to be able to attach jobs to that coroutine via
cardScope.launch { }
, after passing
cardScope
around
Is that possible?
u
Then you will cancel everything that got launched in that scope. If that is what you want, then you can go with your initial code.
r
That's exactly what I want. I want to be able to kill everything in that scope so I have an option to clean up resources etc.
👍 1
u
And what was it about (b)?
b) I want a coroutine being cancelled not kill everything on
cardScope
r
So if a`cardScope.launch { }` dies for whatever reason, it shouldn't tear down everything on
cardScope
u
Ah, I see. Yes, then go with
cardScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
r
Perfect, thanks.