trait AbortSafeFuture {
type Output;
/// Dropping is safe when the future is complete.
fn poll(self: Pin<&mut ManuallyDrop<Self>>, cx: &mut Context<'_>) -> Poll<Self::Output>;
/// Setting the cancellation flag, to make the future complete as early as possible.
/// Can be used to propagate cancellation for futures.
fn cancel(self: Pin<&mut ManuallyDrop<Self>>);
}
edit:
pub trait AbortSafeFuture {
type Output;
/// Similar to Future in std.
///
/// * The caller cannot destruct `Self`, so it need to free resources by itself.
/// * When cancelling inner Future, its `poll_cancel` method needs to be called until it returns' Poll::Ready'. Otherwise, leaks may occur.
fn poll(self: Pin<&mut ManuallyDrop<Self>>, cx: &mut Context<'_>) -> Poll<Self::Output>;
fn poll_cancel(self: Pin<&mut ManuallyDrop<Self>>, cx: &mut Context<'_>) -> Poll<()>;
}
Does this look similar to what you want to achieve?
I have a couple of issues with this:
what are you trying to achieve, why is this reasonable?
ManuallyDrop currently cannot be used as a receiver type and I do not see why we would need it in the type signature here.
you require some invariants to be upheld ("Dropping is safe when the future is complete.") but never use the unsafe keyword, giving the caller a heads up that there are invariants they need to fulfill.
I'm sorry I haven't been able to fill in the details these days. Here are somethings I tried with this API, and you can see the code details in this repo.
The API actually aims to achieve two goals:
Future can sense and handle cancellations, propagating cancellations to the inner Future.
Future cannot drop by RAII, in order to prevent Future from being destructed in an incorrect state.
To meet goal one, there is an additional method called poll_cancel than std's Future, which is called when an cancellation is needed. It implements some propagating cancellation logic.
To achieve goal two, poll_* takes the argument self: Pin<&mut ManuallyDrop<Self>> . So we can't move Self around, and we can't destruct Self around.
Could you explain, what the difference between CompletionFuture and AbortSafeFuture is, because to me it seems like it does the same thing that you want to achieve.
CompletionFuture does this by requiring the caller to promise that they do not drop or forget the future. I think it is weird to wrap it inside of an ManuallyDrop, because you also require the caller to not call e.g. ManuallyDrop::drop/into_inner. But you also place additional burden on implementors of AbortSafeFuture, because to avoid memory leaks, they need to manually drop the future.
async code always satisfies Future, maybe you're thinking of manually implemented Futures that need to be destructed? But then:
they could already be implemented today with some fallbacks, in that case can you implement AbortSafeFuture for them without de-implementing Future (since that would be a breaking change)?
leaks being safe means a bunch of usecases won't be sound, for example io-uring.
You're contradicting yourself here. Does it lead to undefined behaviour (UB) or not?
You're not implementing AbortSafeFuture for the current Futures, you're implementing it for a new type Compat. Thus if runtimes are changed to take an AbortSafeFuture the existing code that uses them with the current Futures will break because it isn't using your Compat type. This is a breaking change.
Yes this should be expected behavior, because you use the DerefMut impl of ManuallyDrop and receive a &mut A from that. If you write to &mut A then you always drop the old value.
ManuallyDrop only guarantees that its contents are not dropped when it is dropped (in the example it is not dropped, because the function takes it by reference).
You could use ptr::write to write without dropping the value.