-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Cancellation problem #2276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The current guarantee on atomic cancellation will be reworked and will not be maintained anymore in the upcoming release. See PR #1937 for details. It also contains updated (rewritten) documentation. |
I had a thoroughly read on #1937. The idea is:
I think #1937 is orthogonal to this issue. Although #1937 redefined the cancellation invariant:
The problem discussed above still exists and is essentially the same. For example:
This implementation is not correct, similar to the very last example in the first post of this issue. If the outer coroutine is cancelled just before This hints the same improvement as Improvement 2 in the first post of this issue. Another example:
This implementation is not correct, either. The reason and workaround is the same as the second example, the timeout-version readFromDiskIntoChannel, in the Problem section of the first post of this issue. This hints the same improvement in the first post of this issue as well. |
I've recently updated docs on how to work around the problem when using timeouts resource to close #2233 |
I had a thoroughly read on #2233 and the updated docs This kind of workaround works to some problem, but doesn't seem to work to some problem, eg., the And, this kind of workaround is more or less anti-structure, counter-intuitive and error-prone as I could see (one have to fully read the To make
|
Background
In most cases, cancellation of a suspend function needs to ensure some program invariant.
For example,
Channel.send
ensures that when this function throws a [CancellationException], it means that the [element] was not sent to this channel.This is a most common kind of invariant and I will use it in the following discuss and name it atomic cancellation according to the comment of this kind of cancellable suspend methods.
Problem
Two simple cancellable methods are implemented as follows:
Unfortunately, the above implementations are both wrong. Either method could be cancelled(timeout) with something sent to its channel, but for different subtle reasons.
Reason 1:
For the second method, if timeout happens just after channel.send returns,
withTimeout
will throwTimeoutCancellationException
instead of returning true. This is because the inner coroutine is cancelled and cancellation supersedes normal return value when calculate the final result of the inner coroutine. This is also true for the fist method.Reason 2:
For the fist method, if the outer coroutine is cancelled just after the inner coroutine returns true, but before the outer dispatcher starts to resume the outer coroutine,
withContext
will throwCancellationException
instead of returning true. This is because the result is cancellable dispatched back.According to the above reasons, the implementations should be modified as follows:
This is complex, counter-structured.
Possible Improvement
Improvement 1: Reason 1 hints that at least in this scene, the inner coroutine is expected to return what the block returns. So, is it possible to provide a new kind of 'Job' to return what coroutine block returns?
Consider that a common pattern of structured concurrency usage is as follows. This kind of
Job
seems reasonable.Improvement 2: Reason 2 hints that at least the library user should be able to control where the coroutine could be cancelled. I think maybe it's better to permit cancellation only by explicit pass true value to a
cancellable: Boolean
parameter ofwithContext
-like methods. Thus, to fulfillment the promise that coroutine cancellation is cooperative.Another Cancellation Problem
I'm recently working with a suspend method as follows:
To implement connecting with timeout, I have to code like:
This is another kind of cancellation invariant that differs from atomic cancellation. But it hints the same improvements as said above.
The text was updated successfully, but these errors were encountered: