owned this note
owned this note
Published
Linked with GitHub
---
title: "Reading notes: A Mechanism for Async Cancellation"
tags: WG-async, reading-club, minutes
date: 2023-11-16
discussion:
url: https://hackmd.io/TbDc4RWCSkGEketBWMLmfA
---
# Reading notes: A Mechanism for Async Cancellation
2023-11-16: "Reading Club / Open Discussion: Async Cancellation" [#323](https://github.com/rust-lang/wg-async/issues/323)
Link to document: https://theincredibleholk.org/blog/2023/11/14/a-mechanism-for-async-cancellation/
## Attendance
- People: TC, eholk, tmandry, swapna iyer
- Minutes: TC
## Return value of `poll_cancel`
TC: This is a nit, but I'd expect `poll_cancel` to return a non-unit value (of a different type than `poll`). So the signature would be:
```rust
pub trait Future {
type Output;
type CancelOutput = (); // Needs a new feature.
fn poll(self: Pin<&mut Self>, cx: Context) -> Poll<Self::Output>;
fn poll_cancel(self: Pin<&mut Self>, cx: Context) -> Poll<Self::CancelOutput> {
Poll::Ready(()) // Needs new magic.
}
}
```
One often needs to know, e.g., whether cancellation happened cleanly, whether a timeout or abort happened, whether some rollback was not possible, etc.
eholk: There's definitely a use case for that, but I think it's also kind of at odds with using `poll_cancel` as a backing mechanism for `async Drop`, since destructors don't return values currently. (Although maybe they should so we can have fallible destructors too?)
TC: Good point. This may be one of those areas where async drop and async cancellation are in fact two different things.
tmandry: Had the same thought. Going in the linear types direction may be another option.
eholk: I really like the "destructors by convention" that you could do if we had a linear type system.
TC: +1.
yosh: -1. I don't think we should go that direction. I think of async as a "portability across domains" mechanism. A kind of portability across effects. So cancellation APIs are a part of that. But the core of it should feel like the same language.
TC: My feeling is that the linear types concepts that we're discussing here would cut across both sync and async.
tmandry: +1. I see these as orthogonal. We could have both. We could have async drop, and we could have the mechanism with explicit destructuring.
yosh: Guaranteed drop and "must use" are maybe two separate things we should distinguish.
tmandry: There is `?Leak`, a type that cannot be leaked, and there is also `?Drop`, and we might want them both. We'd have to think about how painful the transition would be.
eholk: I think these are different. Linear types would be really valuable for sync code also. They give a lot of power that we currently put into destructors just because we don't have a better way to do it right now. With linear types, destructors become more of a convenience.
## Can we really block during unwinding?
tmandry: The post essentially proposes that we `block_on` a future that hasn't finished cancellation during unwinding. Aren't there issues with `block_on` that could lead to deadlocks?
And if we could do this during unwinding, why couldn't we also do it during `drop` of the future itself?
eholk: I didn't work this idea out fully, but my idea is to basically use `catch_unwind` in the `await` desugaring and then use `resume_unwind` after `poll_cancel` is finished, so we'd be able to suspend during unwinding (which I'll admit sounds absolutely terrifying).
tmandry: I've previously thought about how we could do something with poisoning here.
yosh: This could make for a really good use case for `#[no_panic]` functions.
yosh: Separately, could we do something more efficient if we knew that the futures were all on the same thread?
eholk: Even if everything is on the same thread you could still get deadlocks.
tmandry: If you think of a tree of futures, each branch represents a thread, so in the analogy with mutexes, each branch can be poisoned.
yosh: Cool, thank you!
## Recursive cancellation
TC: In the first post it seemed that you were leaning toward recursive cancellation. What are your thoughts about implementing that under this model?
eholk: I'd like to do an experiment that supports recursive cancellation. It does seem strictly more powerful, but that power might not be useful.
TC: More power of this kind is almost always useful.
tmandry: You could do this by returning a future from cancel, or by signaling it.
eholk: There are some challenges with returning a future, due to types and in particular lifetimes, but it does solve a problem with e.g. cancellation on a vec.
tmandry: This is an area where (hand wave) async Drop would be useful.
eholk: ...maybe we could implement IntoCancellableFuture on Vec.
yosh: This is the async-sync-async sandwich but in terms of object lifetimes instead of function calls.
yosh: alternative solution - return a future but mark it as an [`AtomicFuture`](https://blog.yoshuawuyts.com/async-iterator-trait/#cancellation-safety) ("cancellation-safe future").
## Code size
yosh: When I was reasoning through how to implement async Drop, I was worried about the code size - potentially being unbounded. But it seems it's no longer unbounded - just big. Have you measured what the code size overhead is?
yosh: To clarify - I think this will be worth it. I just want to make sure we have a sense of the generated code size.
eholk: I haven't looked into code size at all. I think it is a finite explosion, but finite can still be quite large.