If I have a `CoroutineScope` and then I have dynam...
# coroutines
d
If I have a
CoroutineScope
and then I have dynamically created another scope, how could I add this scope to the first scope, so that second is cancelled along with the first?
j
Structured concurrency actually comes from the
Job
part of the scopes. If you want to establish a parent-child relationship between scopes, you can use their jobs:
Copy code
val someParentScope: CoroutineScope = TODO("get the parent scope from somewhere")

val yourChildScope = CoroutineScope(Job(parent = someParentScope.coroutineContext.job))
That said, child scopes are automatically created by most coroutine builders, why do you need to create one manually?
d
hmmm. my problem is that I have both child and parent scopes constructed at the time. Use case is this: I have a routing module which has its own scope, created upon class construction. And then it has a number of "features", each of which also has its scope and is also constructed separately. And upon receiving the list of "feature" instances in constructor, I want to setup this routerScope • feature1Scope • feature2Scope etc
j
If the feature instances are created separately, it seems strange that the routing module would take care of cancelling them - I would think the creator of the features also owns them and their scopes, and should be responsible for handling their lifecycle. For instance, if the same features should be used again in another instance of router, it could mess things up to have the previous one cancel them. In any case, if you believe this is what you should do, you can probably access the parent job the same way as above and use
someParentScope.coroutineContext.job.invokeOnCompletion { ... }
to register a callback that cancels the child job
d
Yeah, I thought about this a bit more too and came to the similar conclusion: that I need to more carefully adjust lifecycle of those "features". I have something like this in mind:
Copy code
class Feature {
  private var featureScope: CorotineScope

  fun start(parentScope: CoroutineScope) {
    featureScope = parentScope + CoroutineScope()
  }
}

// elsewhere in Router
someFeature.start(routerScope)
// and then 
router.cancel() // cancels feature too
Thank you!
j
Note that if you don't need to cancel the scope of a whole feature at any point (other than from the parent), you could actually use the parent scope directly instead of creating a child
d
oh, right! But in my case I also customize the feature scope a bit: install SupervisorJob() and also custom exception handler...
j
Ah yeah if you need a custom context, then it makes sense
n
Just to clarify, in your code snippet, featureScope is not a child of parentScope.
Copy code
featureScope = parentScope + CoroutineScope()
CoroutineScope()
creates a
Job
which is not connected to
parentScope
at all and that will overwrite the
Job
in
parentScope
when creating
featureScope
.
d
thanks for clarification. I've rechecked just in case. I have defined it like this:
Copy code
featureScope = CoroutineScope(SupervisorJob(parentScope?.coroutineContext?.job) + handler + dispatcher)
which ties supervisor job to the parent scope's job if provided.