diff --git a/futures-core/src/future/future_obj.rs b/futures-core/src/future/future_obj.rs index d76770a2b9..c1e143f1e6 100644 --- a/futures-core/src/future/future_obj.rs +++ b/futures-core/src/future/future_obj.rs @@ -1,4 +1,5 @@ use core::{ + mem, fmt, future::Future, marker::PhantomData, @@ -13,22 +14,32 @@ use core::{ /// take `dyn Trait` by value and `Box` is not available in no_std /// contexts. pub struct LocalFutureObj<'a, T> { - ptr: *mut (), - poll_fn: unsafe fn(*mut (), &mut Context<'_>) -> Poll, - drop_fn: unsafe fn(*mut ()), + future: *mut (dyn Future + 'static), + drop_fn: unsafe fn(*mut (dyn Future + 'static)), _marker: PhantomData<&'a ()>, } impl Unpin for LocalFutureObj<'_, T> {} +unsafe fn remove_future_lifetime<'a, T>(ptr: *mut (dyn Future + 'a)) + -> *mut (dyn Future + 'static) +{ + mem::transmute(ptr) +} + +unsafe fn remove_drop_lifetime<'a, T>(ptr: unsafe fn (*mut (dyn Future + 'a))) + -> unsafe fn(*mut (dyn Future + 'static)) +{ + mem::transmute(ptr) +} + impl<'a, T> LocalFutureObj<'a, T> { /// Create a `LocalFutureObj` from a custom trait object representation. #[inline] pub fn new + 'a>(f: F) -> LocalFutureObj<'a, T> { LocalFutureObj { - ptr: f.into_raw(), - poll_fn: F::poll, - drop_fn: F::drop, + future: unsafe { remove_future_lifetime(f.into_raw()) }, + drop_fn: unsafe { remove_drop_lifetime(F::drop) }, _marker: PhantomData, } } @@ -61,9 +72,9 @@ impl Future for LocalFutureObj<'_, T> { type Output = T; #[inline] - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { unsafe { - ((*self).poll_fn)((*self).ptr, cx) + Pin::new_unchecked(&mut *self.future).poll(cx) } } } @@ -71,7 +82,7 @@ impl Future for LocalFutureObj<'_, T> { impl Drop for LocalFutureObj<'_, T> { fn drop(&mut self) { unsafe { - (self.drop_fn)(self.ptr) + (self.drop_fn)(self.future) } } } @@ -119,92 +130,123 @@ impl Future for FutureObj<'_, T> { } /// A custom implementation of a future trait object for `FutureObj`, providing -/// a hand-rolled vtable. +/// a vtable with drop support. /// /// This custom representation is typically used only in `no_std` contexts, /// where the default `Box`-based implementation is not available. /// -/// The implementor must guarantee that it is safe to call `poll` repeatedly (in -/// a non-concurrent fashion) with the result of `into_raw` until `drop` is -/// called. +/// # Safety +/// +/// See the safety notes on individual methods for what guarantees an +/// implementor must provide. pub unsafe trait UnsafeFutureObj<'a, T>: 'a { - /// Convert an owned instance into a (conceptually owned) void pointer. - fn into_raw(self) -> *mut (); - - /// Poll the future represented by the given void pointer. + /// Convert an owned instance into a (conceptually owned) fat pointer. /// /// # Safety /// - /// The trait implementor must guarantee that it is safe to repeatedly call - /// `poll` with the result of `into_raw` until `drop` is called; such calls - /// are not, however, allowed to race with each other or with calls to - /// `drop`. - unsafe fn poll(ptr: *mut (), cx: &mut Context<'_>) -> Poll; + /// ## Implementor + /// + /// The trait implementor must guarantee that it is safe to convert the + /// provided `*mut (dyn Future + 'a)` into a `Pin<&mut (dyn + /// Future + 'a)>` and call methods on it, non-reentrantly, + /// until `UnsafeFutureObj::drop` is called with it. + fn into_raw(self) -> *mut (dyn Future + 'a); - /// Drops the future represented by the given void pointer. + /// Drops the future represented by the given fat pointer. /// /// # Safety /// + /// ## Implementor + /// /// The trait implementor must guarantee that it is safe to call this - /// function once per `into_raw` invocation; that call cannot race with - /// other calls to `drop` or `poll`. - unsafe fn drop(ptr: *mut ()); + /// function once per `into_raw` invocation. + /// + /// ## Caller + /// + /// The caller must ensure: + /// + /// * the pointer passed was obtained from an `into_raw` invocation from + /// this same trait object + /// * the pointer is not currently in use as a `Pin<&mut (dyn Future + 'a)>` + /// * the pointer must not be used again after this function is called + unsafe fn drop(ptr: *mut (dyn Future + 'a)); } unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for &'a mut F where F: Future + Unpin + 'a { - fn into_raw(self) -> *mut () { - self as *mut F as *mut () + fn into_raw(self) -> *mut (dyn Future + 'a) { + self as *mut dyn Future } - unsafe fn poll(ptr: *mut (), cx: &mut Context<'_>) -> Poll { - let p: Pin<&mut F> = Pin::new_unchecked(&mut *(ptr as *mut F)); - F::poll(p, cx) + unsafe fn drop(_ptr: *mut (dyn Future + 'a)) {} +} + +unsafe impl<'a, T> UnsafeFutureObj<'a, T> for &'a mut (dyn Future + Unpin + 'a) +{ + fn into_raw(self) -> *mut (dyn Future + 'a) { + self as *mut dyn Future } - unsafe fn drop(_ptr: *mut ()) {} + unsafe fn drop(_ptr: *mut (dyn Future + 'a)) {} } unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Pin<&'a mut F> where F: Future + 'a { - fn into_raw(mut self) -> *mut () { - let mut_ref: &mut F = unsafe { Pin::get_unchecked_mut(self.as_mut()) }; - mut_ref as *mut F as *mut () + fn into_raw(self) -> *mut (dyn Future + 'a) { + unsafe { Pin::into_inner_unchecked(self) as *mut dyn Future } } - unsafe fn poll(ptr: *mut (), cx: &mut Context<'_>) -> Poll { - let future: Pin<&mut F> = Pin::new_unchecked(&mut *(ptr as *mut F)); - F::poll(future, cx) + unsafe fn drop(_ptr: *mut (dyn Future + 'a)) {} +} + +unsafe impl<'a, T> UnsafeFutureObj<'a, T> for Pin<&'a mut (dyn Future + 'a)> +{ + fn into_raw(self) -> *mut (dyn Future + 'a) { + unsafe { Pin::into_inner_unchecked(self) as *mut dyn Future } } - unsafe fn drop(_ptr: *mut ()) {} + unsafe fn drop(_ptr: *mut (dyn Future + 'a)) {} } #[cfg(feature = "alloc")] mod if_alloc { use super::*; - use core::mem; use alloc::boxed::Box; unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Box where F: Future + 'a { - fn into_raw(self) -> *mut () { - Box::into_raw(self) as *mut () + fn into_raw(self) -> *mut (dyn Future + 'a) { + Box::into_raw(self) } - unsafe fn poll(ptr: *mut (), cx: &mut Context<'_>) -> Poll { - let ptr = ptr as *mut F; - let pin: Pin<&mut F> = Pin::new_unchecked(&mut *ptr); - F::poll(pin, cx) + unsafe fn drop(ptr: *mut (dyn Future + 'a)) { + drop(Box::from_raw(ptr as *mut F)) } + } - unsafe fn drop(ptr: *mut ()) { - drop(Box::from_raw(ptr as *mut F)) + unsafe impl<'a, T: 'a> UnsafeFutureObj<'a, T> for Box + 'a> { + fn into_raw(self) -> *mut (dyn Future + 'a) { + Box::into_raw(self) + } + + unsafe fn drop(ptr: *mut (dyn Future + 'a)) { + drop(Box::from_raw(ptr)) + } + } + + unsafe impl<'a, T: 'a> UnsafeFutureObj<'a, T> for Box + Send + 'a> { + fn into_raw(self) -> *mut (dyn Future + 'a) { + Box::into_raw(self) + } + + unsafe fn drop(ptr: *mut (dyn Future + 'a)) { + drop(Box::from_raw(ptr)) } } @@ -212,22 +254,44 @@ mod if_alloc { where F: Future + 'a { - fn into_raw(mut self) -> *mut () { - let mut_ref: &mut F = unsafe { Pin::get_unchecked_mut(self.as_mut()) }; - let ptr = mut_ref as *mut F as *mut (); - mem::forget(self); // Don't drop the box - ptr + fn into_raw(self) -> *mut (dyn Future + 'a) { + Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }) + } + + unsafe fn drop(ptr: *mut (dyn Future + 'a)) { + drop(Pin::from(Box::from_raw(ptr))) + } + } + + unsafe impl<'a, T: 'a> UnsafeFutureObj<'a, T> for Pin + 'a>> { + fn into_raw(self) -> *mut (dyn Future + 'a) { + Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }) + } + + unsafe fn drop(ptr: *mut (dyn Future + 'a)) { + drop(Pin::from(Box::from_raw(ptr))) + } + } + + unsafe impl<'a, T: 'a> UnsafeFutureObj<'a, T> for Pin + Send + 'a>> { + fn into_raw(self) -> *mut (dyn Future + 'a) { + Box::into_raw(unsafe { Pin::into_inner_unchecked(self) }) } - unsafe fn poll(ptr: *mut (), cx: &mut Context<'_>) -> Poll { - let ptr = ptr as *mut F; - let pin: Pin<&mut F> = Pin::new_unchecked(&mut *ptr); - F::poll(pin, cx) + unsafe fn drop(ptr: *mut (dyn Future + 'a)) { + drop(Pin::from(Box::from_raw(ptr))) } + } - unsafe fn drop(ptr: *mut ()) { - #[allow(clippy::cast_ptr_alignment)] - drop(Pin::from(Box::from_raw(ptr as *mut F))); + impl<'a, F: Future + Send + 'a> From> for FutureObj<'a, ()> { + fn from(boxed: Box) -> Self { + FutureObj::new(boxed) + } + } + + impl<'a> From + Send + 'a>> for FutureObj<'a, ()> { + fn from(boxed: Box + Send + 'a>) -> Self { + FutureObj::new(boxed) } } @@ -237,20 +301,32 @@ mod if_alloc { } } - impl<'a, F: Future + Send + 'a> From> for FutureObj<'a, ()> { - fn from(boxed: Box) -> Self { + impl<'a> From + Send + 'a>>> for FutureObj<'a, ()> { + fn from(boxed: Pin + Send + 'a>>) -> Self { FutureObj::new(boxed) } } + impl<'a, F: Future + 'a> From> for LocalFutureObj<'a, ()> { + fn from(boxed: Box) -> Self { + LocalFutureObj::new(boxed) + } + } + + impl<'a> From + 'a>> for LocalFutureObj<'a, ()> { + fn from(boxed: Box + 'a>) -> Self { + LocalFutureObj::new(boxed) + } + } + impl<'a, F: Future + 'a> From>> for LocalFutureObj<'a, ()> { fn from(boxed: Pin>) -> Self { LocalFutureObj::new(boxed) } } - impl<'a, F: Future + 'a> From> for LocalFutureObj<'a, ()> { - fn from(boxed: Box) -> Self { + impl<'a> From + 'a>>> for LocalFutureObj<'a, ()> { + fn from(boxed: Pin + 'a>>) -> Self { LocalFutureObj::new(boxed) } } diff --git a/futures-core/src/lib.rs b/futures-core/src/lib.rs index fbffb63653..43e7dcff57 100644 --- a/futures-core/src/lib.rs +++ b/futures-core/src/lib.rs @@ -1,6 +1,6 @@ //! Core traits and types for asynchronous operations in Rust. -#![feature(futures_api)] +#![feature(futures_api, pin_into_inner)] #![cfg_attr(feature = "cfg-target-has-atomic", feature(cfg_target_has_atomic))] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index dac20f83e4..851fdd92c8 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -493,8 +493,8 @@ pub trait FutureExt: Future { /// Wrap the future in a Box, pinning it. #[cfg(feature = "alloc")] - fn boxed(self) -> BoxFuture<'static, Self::Output> - where Self: Sized + Send + 'static + fn boxed<'a>(self) -> BoxFuture<'a, Self::Output> + where Self: Sized + Send + 'a { Box::pin(self) } diff --git a/futures/tests/future_obj.rs b/futures/tests/future_obj.rs index b914043463..85c9fcad51 100644 --- a/futures/tests/future_obj.rs +++ b/futures/tests/future_obj.rs @@ -6,7 +6,7 @@ use futures::task::{Context, Poll}; #[test] fn dropping_does_not_segfault() { - FutureObj::new(Box::new(async { String::new() })); + FutureObj::new(async { String::new() }.boxed()); } #[test] @@ -29,7 +29,7 @@ fn dropping_drops_the_future() { } } - FutureObj::new(Box::new(Inc(&mut times_dropped))); + FutureObj::new(Inc(&mut times_dropped).boxed()); assert_eq!(times_dropped, 1); }