Scott Kruse
03/15/2021, 4:03 PMReceiveChannel. MainScope is a basic scope tied to Dispatchers.Main -- For some reason, despite calling cancel() from the calling viewmodel's onCleared function, This job continues to run after back pressing and reentering the app. If i change the scope to viewmodelScope the coroutine is cancelled appropriately. Can anyone shed some light on this?
fun run(params: Params, onEachResult: (any: Any) -> Unit) {
mainScope.job = mainScope.launch {
run(params).consumeEach(onEachResult)
}
}
fun cancel() {
mainScope.job.cancel()
}
internal class MainScope(private val mainContext: CoroutineContext) : CoroutineScope {
var job: Job = Job()
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
showError(throwable)
}
override val coroutineContext: CoroutineContext = mainContext + job + exceptionHandler
fun showError(t: Throwable) {
Timber.e(t)
throw t
}
}Zach Klippenstein (he/him) [MOD]
03/15/2021, 4:19 PMrun getting called multiple times? If it is, by the time cancel is called, mainScope.job will be the leaf job in a tree of jobs, and since you have no reference to the scope’s root job, there’s no way to cancel any of the other jobs.
The implementation of MainScope is very surprising – it’s highly unusual for a `CoroutineScope`’s job to change after it’s created. This makes the code very weird to reason about – I would make job immutable. It makes the scope effectively disable structured concurrency, which is pretty dangerous (and I’m guessing somehow the cause for your bug).Scott Kruse
03/15/2021, 4:27 PMrun has the potential to be called multiple times
var job: Job = Job()
val coroutineContext: CoroutineContext = mainContext + job + exceptionHandler
Would this not imply the scope's root job?Zach Klippenstein (he/him) [MOD]
03/15/2021, 4:35 PMcancel.Zach Klippenstein (he/him) [MOD]
03/15/2021, 4:35 PMmainScope.job, not mainScope.coroutineContext.job.Zach Klippenstein (he/him) [MOD]
03/15/2021, 4:36 PMMainScope is a CoroutineScope, you can also just call mainScope.cancel(), which calls coroutineScope.job.cancel() under the hood. This confusion is part of the reason why MainScope is smelly.Scott Kruse
03/15/2021, 4:45 PMmainScope.job and calling mainScope.job.cancel() this is not sufficiently canceling since the job has become a leaf job potentially if run is called twice? If run was only to be called once it would behave ok since it would be the only job mainScope has launched
mainScope.job = mainScope.launch {
run(params).consumeEach(onEachResult)
}Zach Klippenstein (he/him) [MOD]
03/15/2021, 4:48 PMcancelChildren?Scott Kruse
03/15/2021, 4:51 PMMainScope implementation as it's literally only used for this one job so imo there's really not a need to have a dedicated scope that's not lifecycle aware.Zach Klippenstein (he/him) [MOD]
03/15/2021, 4:53 PM