Skip to content

Deprecate NonCancellable in favor of a new scoped function #4643

@dkhalanskyjb

Description

@dkhalanskyjb

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions