Edoardo Luppi
10/06/2023, 9:01 PMoverride fun disposeAsync(): Promise<Unit> =
coroutineScope.promise {
delegate.dispose() // This is the original suspending method I'm wrapping
// TODO: are we sure this will work?
// It doesn't feel right because we are cancelling
// children inside one of those children
coroutineScope.coroutineContext.cancelChildren()
}
Note the TODO I had left in there. This obviously will throw an exception when run.
Any suggestion? Maybe I should switch to GlobalScope
?Sam
10/06/2023, 9:21 PMdispose
on cancellation. As soon as you create the scope, launch a job containing a finally
block that will run after the scope is cancelled.
coroutineScope.launch(start = ATOMIC) {
try {
awaitCancellation()
} finally {
withContext(NonCancellable) {
delegate.dispose()
}
}
}
Then disposeAsync()
can simply call coroutineScope.cancel()
and it will automatically run the dispose
during the cancellation.Edoardo Luppi
10/06/2023, 9:24 PMATOMIC
, and why the NonCancellable
?Edoardo Luppi
10/06/2023, 9:28 PMSam
10/06/2023, 9:31 PMSam
10/06/2023, 9:33 PMNonCancellable
is so that dispose will still be able to run even though the scope was already cancelledEdoardo Luppi
10/06/2023, 9:34 PMPromise
, I can await it, and that means processing will go on only after the delegate
has been disposed. Not sure how that will work with this approach.Sam
10/06/2023, 9:36 PMcancelAndJoin
to wait for the dispose call to complete. Join will wait for all jobs in the scope to finish, even after cancellation.Sam
10/06/2023, 9:41 PMEdoardo Luppi
10/06/2023, 9:42 PMSam
10/06/2023, 9:43 PMEdoardo Luppi
10/06/2023, 9:43 PMcoroutineScope.promise {
try {
coroutineScope.coroutineContext.cancelChildren()
awaitCancellation()
} finally {
withContext(NonCancellable) {
delegate.dispose()
}
}
}
Edoardo Luppi
10/06/2023, 9:43 PMEdoardo Luppi
10/06/2023, 9:48 PMEdoardo Luppi
10/06/2023, 10:03 PMoverride fun disposeAsync(): Promise<Unit> =
coroutineScope.promise {
try {
coroutineContext.cancelChildren()
} finally {
withContext(NonCancellable) {
delegate.dispose()
}
}
}
I'm also trying in another way, by storing the dormant dispose job and returning it from disposeAsync
.Edoardo Luppi
10/06/2023, 10:23 PMcoroutineContext.cancelChildren()
I use
cancel()
to cancel the entire scope, the awaited Promise
will throw an error.
---
Another approach that seem to work.
@OptIn(ExperimentalCoroutinesApi::class)
private val disposeJob = coroutineScope.launch(start = CoroutineStart.ATOMIC) {
try {
awaitCancellation()
} finally {
withContext(NonCancellable) {
delegate.dispose()
}
}
}
...
override fun disposeAsync(): Promise<Unit> {
coroutineScope.cancel()
@OptIn(DelicateCoroutinesApi::class)
return GlobalScope.promise {
disposeJob.cancelAndJoin()
}
}
Sam
10/10/2023, 7:35 AMEdoardo Luppi
10/10/2023, 7:37 AMEdoardo Luppi
10/10/2023, 7:38 AMSam
10/10/2023, 7:47 AMJob()
or SupervisorJob()
, that job itself won't have a coroutine or resources, it will just be used as the parent for all the other jobs. So the only difference is that cancelling the scope prevents any new coroutines from being started in that scope, whereas just cancelling the children means you would be able to continue using the scope to launch new coroutines later.Edoardo Luppi
10/10/2023, 7:50 AMJob
or SupervisorJob
. My main concern was about the allocated Dispatcher tho. I use an IO.limitedParallelism
dispatcher.Sam
10/10/2023, 8:04 AM