Closed jxdabc closed 4 months ago
You mention io_uring and IOCP in your initial motivation, but your proposal does not actually address those use-cases?
Ultimately, I don't have the bandwidth to support a project like this in Tokio. You will probably have to prototype it elsewhere.
@Darksonn
For addressing IOCP use-case, @Matthias247 has given an example here https://github.com/rust-lang/futures-rs/issues/1278#issuecomment-427182404. The only remaining problem is how to avoid blocking scheduler threads, which is addressed by this proposal.
For prototype, because this approach needs scheduler and select
to opt in. I could
prototype it by a PR prototype, if it would be helpful.
I'm not sure how well the issue was understood in 2018, but it's understood well these days that for io_uring/IOCP, you must either use owned buffers (in which case you don't need any kind of AsyncDrop), or you need some way to say that the read/write future cannot be mem::forgot
ten, which requires language changes. Anything else is unsound.
I could prototype it by a PR prototype, if it would be helpful.
Sorry, it looks like I was not clear enough. Tokio is not going to accept a PR for this proposal. I will close this issue now.
This is inspired by all the hard work of @Matthias247.
Background
AsyncDrop is a feature that is very needed in many scenarios. Among them, the widely discussed ones include:
On the other hand, AsyncDrop is HARD to design and never reaches its RFC state, since 2019.
Serious CHALLENGES include, e.g., how to forbid cancelling of async-drops, how to prevent access to an in-progress dropping struct. And the async WG goal is to do those without any support of runtimes, which makes it more challenge.
The final design, if we managed to get there, would probably be either imperfect or complex.
Discussion
There is yet an alternative to AsyncDrop, which makes clean-up work async.
The idea is make futures able to OPT IN to:
The specific goal is:
Surprisingly, when come to the design, it is rather intuitively straight forward. It requires nothing magic or complex, and no compiler support is ever needed. That makes me believe it is probably a right approach to async cancellation.
Design
On the CANCEL-EE (typically an "atomic" Future) side, it needs an API to know it is canceled.
The shape would be:
The intended usage would be:
On the CANCEL-ER side (typically a scheduler, or some Future combiner, such as select), it needs an API to tell its descendants about the cancellation, and poll them if necessary.
The shape would be:
The intended usage would be:
That's all. Suddenly, all the goals we mentioned above is satisfied.
Analysis
First, let me explain a little about the 2 added API.
with_cancel_state()
returnstrue
if any descendent ofchild_future
calledis_cancelled()
.child_future
until its descendents finish their async-cancel.is_cancelled()
andwith_cancel_state()
will return false.is_cancelled()
just receives the parentcancel_child
state, as you can see.Let's check whether all the goals mentioned above is satisfied.
is_cancelled()
, NO EXTRA POLL is added.join
, need do nothing to opt in. (They just pass through the parent cancel state and poll).select
, need to usewith_cancel_state()
to opt in. But if it's not, its descends still partially get the async-clean-up ability from its ancestors. (If the cancellation is initialed by its ancestors)with_cancel_state()
to opt in. But if it's not, some descents still get the async-clean-up ability. (If they are under an opt-in combiner that initialed the cancellation)Implementation
I won't detail the implementation, because it is obvious.
On the opt-in side,