https://kotlinlang.org logo
#coroutines
Title
# coroutines
r

reactormonk

10/31/2023, 4:39 PM
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

uli

11/01/2023, 3:54 PM
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

reactormonk

11/01/2023, 3:54 PM
Pretty much. You get a
TagLost
after each card message.
I also considered
SupervisorJob
, would that also work?
u

uli

11/01/2023, 3:56 PM
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

reactormonk

11/01/2023, 3:57 PM
So instead of a
cardScope
, better use a
cardJob
and use
SupervisorJob
to create that.
u

uli

11/01/2023, 3:59 PM
I was thinking more like this:
Copy code
cardScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
r

reactormonk

11/01/2023, 4:00 PM
Also an option 🤔
u

uli

11/01/2023, 4:01 PM
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

reactormonk

11/01/2023, 4:02 PM
I assumed
cardJob = SupervisorJob()
u

uli

11/01/2023, 4:03 PM
Maybe forget about the card job, it breaks structured concurrency.
r

reactormonk

11/01/2023, 4:03 PM
Ok.
So
cardJob = SupervisorJob()
and then cancel the
cardJob
, so it'll stop all coroutines inside the cards?
u

uli

11/01/2023, 4:06 PM
I do not really understand. how do you want to conenct the coroutines to the card job?
r

reactormonk

11/01/2023, 4:06 PM
CoroutineScope(Dispatchers.Default + cardJob)
Or maybe I didn't fully grasp the "breaks structural concurrency" part
u

uli

11/01/2023, 4:07 PM
oh, that’s equivalent. scope.cancel call scope.job.cancel (pseudocode)
r

reactormonk

11/01/2023, 4:07 PM
... I assumed it meant "the cancel's gonna break if one of the started coroutines misbehaves"
u

uli

11/01/2023, 4:07 PM
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

reactormonk

11/01/2023, 4:09 PM
I can recreate it though, when I make a new
SupervisorJob()
?
u

uli

11/01/2023, 4:09 PM
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

reactormonk

11/01/2023, 4:10 PM
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

uli

11/01/2023, 4:18 PM
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

reactormonk

11/01/2023, 4:19 PM
How would I use that scope downstream?
u

uli

11/01/2023, 4:20 PM
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

reactormonk

11/01/2023, 4:21 PM
I'd prefer to be able to attach jobs to that coroutine via
cardScope.launch { }
, after passing
cardScope
around
Is that possible?
u

uli

11/01/2023, 4:21 PM
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

reactormonk

11/01/2023, 4:22 PM
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

uli

11/01/2023, 4:23 PM
And what was it about (b)?
b) I want a coroutine being cancelled not kill everything on
cardScope
r

reactormonk

11/01/2023, 4:24 PM
So if a`cardScope.launch { }` dies for whatever reason, it shouldn't tear down everything on
cardScope
u

uli

11/01/2023, 4:24 PM
Ah, I see. Yes, then go with
cardScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
r

reactormonk

11/01/2023, 4:25 PM
Perfect, thanks.