NonCancellable is problematic.
First, it can be passed to launch/async/produce/etc. and break structured concurrency. This is not what you want: see #4435 for details.
The only correct usage of NonCancellable is as the withContext parameter. This is an idiom:
try {
// do something
} finally {
withContext(NonCancellable) {
// cleaning up resources whose `close` is `suspend`
}
}
NonCancellable is not particularly good at that, as well. withContext uses prompt cancellation. If withContext(NonCancellable) suspends on the final } (waiting for its child coroutines, or dispatching back to the original dispatcher), then it will throw a CancellationException on exit. That will override the original exception thrown inside try: https://pl.kotl.in/u5gIaCPQ5
What we need instead is:
suspend fun <T> nonCancellable(action: suspend CoroutineScope.() -> T): T =
withContext(NonCancellable) {
coroutineScope {
action()
}
}
This looks like the only reasonable usage pattern for NonCancellable, so the additional flexibility presented by it being a CoroutineContext element is harmful.
NonCancellableis problematic.First, it can be passed to
launch/async/produce/etc. and break structured concurrency. This is not what you want: see #4435 for details.The only correct usage of
NonCancellableis as thewithContextparameter. This is an idiom:NonCancellableis not particularly good at that, as well.withContextuses prompt cancellation. IfwithContext(NonCancellable)suspends on the final}(waiting for its child coroutines, or dispatching back to the original dispatcher), then it will throw aCancellationExceptionon exit. That will override the original exception thrown insidetry: https://pl.kotl.in/u5gIaCPQ5What we need instead is:
This looks like the only reasonable usage pattern for
NonCancellable, so the additional flexibility presented by it being aCoroutineContextelement is harmful.