Proposal
Add the following method to the Future
trait:
fn poll_cancel(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
Poll::Ready(())
}
The result would be:
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
fn poll_cancel(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
Poll::Ready(())
}
}
Future contract
The new contract for Future is:
- All futures are cancellable.
- Once
poll_cancel
is called,poll
may never be called again. - A polled future may not be dropped without
poll_cancel
returning ready.
Guarantees of poll_cancel
:
- Cancelling an unpolled future is safe.
- Cancelling a finished future is safe.
- Cancelling a cancelled future is safe.
Thus, a future must either be driven to completion or be cancelled before it can get dropped. Also, any future can be safely cancelled at any moment in its lifetime.
Motivation
Currently futures are all assumed to be trivially cancellable. This is to say
that the can be cancelled in a synchronous operation (drop
). However, this abstraction
has turned out to be too limitating for certain asynchronous APIs, including, most notably io_uring
.
These APIs require cancellation to be an asynchronous operation (async drop
).
Providing a poll_cancel
method to the Future trait will help the asynchronous ecosystem to
migrate to a more flexible abstraction for future cancellation.
Async
Asynchronous functions and blocks will have to generate a state machine that can keep track of a future in mid cancellation as a possible state.
Compatibility
Even though adding a default method to a trait is not a breaking change,
changing the contract for the Future
trait is breaking. This however, should only affect crates
implementing a runtime, which are a minority. Libraries implementing futures will not be affected,
since all futures already adhere to this contract.
Related topics
- Asynchronous Destructors
- Async Cancellation I
- Notes on io-uring - Without boats, dreams dry up
- Asynchronous Destructors - Without boats, dreams dry up
EDIT:
When I say "safe", I don't mean "memory safe", I mean safe from panicking or blocking. The entire API should always be memory safe.