Skip to content

PoC onCancelRequested#4633

Open
reardonj wants to merge 26 commits into
typelevel:series/3.xfrom
reardonj:4620-onCancelRequested
Open

PoC onCancelRequested#4633
reardonj wants to merge 26 commits into
typelevel:series/3.xfrom
reardonj:4620-onCancelRequested

Conversation

@reardonj

@reardonj reardonj commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Basic implementation of onCancelRequested for IO. This version uses existing IO combinators instead of inlining the entire implementation in IOFiber. It also runs the ack asynchronously as that was easier to implement. the alternative would end up involving something that looks suspiciously like a stack of fiber stacks, since an onCancelRequested handler could have another onCancelRequested inside it. Just starting a new fiber for each feels much safer.

Also adds a Fiber.joinOrCancel implementation based on this new method.

Fixes #4620


TODOs

  • update cancelation documentation
  • Do we need new async* combinators, or should the current combinators move to the new design? I broke a test trying to migrate fromFutureCancelable as the test scenario never actually killed the fiber. I'm sure there are other bad usages of async* in real code, so I am hesitant to just update the existing combinators. I don't think we need something like asyncFull with both onCancelRequested and onCancel callbacks, since one way or another the FFI should be done after the callback completes.
  • Optimize. I haven't benchmarked this at all, and I'm sure we can do some optimization in IOFiber.

Basic implementation of onCancelRequested for IO. This version uses existing IO combinators instead of inlining the entire implementation in IOFiber. It also runs the `ack` asynchronously as that was easier to implement.

Also adds a `Fiber.joinOrCancel` implementation based on this new method.
reardonj added 23 commits July 9, 2026 23:08
A fiber can be canceled before it starts. Use a Deferred to make sure we have registered the onCancelRequested callback before cancelling the fiber
You cannot acknowledge cancelation after finalization has started. Which makes perfect sense in hindsight. This can happen if you register an `onCancelRequested` handler inside a `onCancel` handler, which is utterly uncancelable, which seems silly, but happened after I changed `Async.both` to use `joinOrCancel`

This comes up in `Resource.both`, which uses `F.both` to run finalizers, which the `Async` implementation of uses the `join.onCancel(cancel)` construct.

Changing `both` to use `joinOrCancel` means the implementation observed cancelation inside `onCancel`, at which point we are obviously never canceling anything. `both` is fundamentally lossy, but `joinOrCancel` will at least eliminate edge cases where the user might be able to observe that both sides obviously completed, but it still got canceled.

The fix is a simple addition to the `acknowledgeCancelation` checks.
`par` is the prefix used elsewhere in CE to indicate when actions can happen concurrently, and `async` has a specific meaning: the async FFI.
Same race condition as cancelation after suspending to await the callback, if suspended then needs to ack we need to try resuming as well.
The change in raceOutcome behavior causes this to actually return a result when the fiber is canceled now, and the flatMap is evaluated to the next IO before cancelation actually takes effect, triggering a crash from the sys.error side effect. Wrapping it so it is properly suspended then ignored on cancelation.
If the fiber is canceled at this point, the ack isn't getting popped anyways.
This failed on one build for no particular reason, which I suspect is a CI issue and not an issue with the changes to race
- Don't spawn an extra fiber to cancel
- poll when the first side of race cancels
@reardonj reardonj marked this pull request as ready for review July 13, 2026 01:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Fiber#joinOrCancel

1 participant