Repository navigation
Lossless cancelation - #4710
Draft
reardonj wants to merge 9 commits into
Draft
Lossless cancelation#4710reardonj wants to merge 9 commits into
reardonj wants to merge 9 commits into
Conversation
The sys.error call is a side-effect and should be suspended in IO
- add onCancelContinueWith that can temporarily revert cancelation to MonadCancel and IO - replace cancelation stack with full unwinding to support continuing from cancelation - add equivalent methods for Async - use onCancelContinueWith to make racePair and other combinators cancelation-safe
reardonj
force-pushed
the
4620-onCancelContinueWith
branch
from
November 4, 2026 04:36
ce37681 to
6bb7389
Compare
reardonj
force-pushed
the
4620-onCancelContinueWith
branch
from
November 4, 2026 04:49
6bb7389 to
cfd23db
Compare
"parTraverseN - interrupt on errors" failed in CI on: macos-14, 2.13.18, temurin@17, ciJVM Local running of the parTraverse benchmarks don't show an significant change in speed between branch and series/3.x, so I'm chalking this up to flakey CI and reducing repetitions.
PureConc really needs async cancelation to be implemented to get that to work.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR provides an alternate solution to #4641 to fix #4571 , #4489 and related issues.
This solution avoids adding a completely different cancelation mechanism by breaking the fundamental principles of cancelation, by letting us 'continue':
onCancelContinueWithworks likeonCancelexcept:This let's us write idioms like:
poll(f.join).onCancelContinueWith(f.cancel *> f.join)To express that if the fiber is canceled, another fiber,
f, should also be canceled, but still needs to be joined to the current fiber.Implementation
onCancelContinueWithis defined inMonadCancel, with a default implementation that degrades toonCancel. This results in fairly clean fallback behavior (no worse than in CE 3.7.x) for implementations that don't define their ownonCancelContinueWith.This required some reworking of how cancelation works. There is no longer a separate stack of finalizers in IOFiber1. Instead, the stack is unwound until an
onCancelContinueWithoronCancelis hit. In either case, the handler is then executed. Then in theonCancelContinueWithcase, the fiber continues executing instead of continuing cancelation. However, the fiber is still canceled! So we'll immediately continue unwinding unless cancellation is masked whereonCancelContinueWith's handler was run. If cancellation is masked, the fiber will continue running until unmasked, then start unwinding again.I've also written
async*equivalents that complete on cancelation and made use of them in a few places.Compared to #4641
Since
onCancelContinueWithstill requirespoll, this design avoids the pitfall of #4641 whereonCancelRequesteddidn't actually need poll, but it was needed to make things work with the bad defaultonCancelRequested.This design is less powerful, as the stack has to get unwound to the handler before continuing, so state that was set up in the poll is lost before the handler goes off. This means usages tend to be a bit repetitive (eg. the
joinexample above), where both the poll body and handler have similar content.On the upside, laws for the new method are fairly obvious, as they mostly follow the cancel laws.
What is Not Fixed
cancelable, but does not avoid the separate fiber. If anything, this probably pushes us further away from removing the fiber by removing the separate finalizer stackjoinOrCancelas I can't see a way to do it without passing in apoll, which feels rather ugly. Instead I have added some methods to attach one fiber to another, so their cancelations are ordered properly.TODO
Footnotes
As a bonus, under
-XX:+UseCompactObjectHeaders/ JVM 27, this fixes Can we push to makeIOFiberfit in 64 bytes? #3584 by pushing the object size to 64 bytes! ↩