Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change FutureObj to use a *mut dyn Future fat pointer #1550

Merged
merged 1 commit into from
Apr 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 146 additions & 63 deletions futures-core/src/future/future_obj.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::{
mem,
fmt,
future::Future,
marker::PhantomData,
Expand All @@ -13,22 +14,33 @@ use core::{
/// take `dyn Trait` by value and `Box<dyn Trait>` is not available in no_std
/// contexts.
pub struct LocalFutureObj<'a, T> {
ptr: *mut (),
poll_fn: unsafe fn(*mut (), &mut Context<'_>) -> Poll<T>,
drop_fn: unsafe fn(*mut ()),
future: *mut (dyn Future<Output = T> + 'static),
drop_fn: unsafe fn(*mut (dyn Future<Output = T> + 'static)),
_marker: PhantomData<&'a ()>,
}

impl<T> Unpin for LocalFutureObj<'_, T> {}

#[allow(clippy::transmute_ptr_to_ptr)]
unsafe fn remove_future_lifetime<'a, T>(ptr: *mut (dyn Future<Output = T> + 'a))
-> *mut (dyn Future<Output = T> + 'static)
{
mem::transmute(ptr)
}

unsafe fn remove_drop_lifetime<'a, T>(ptr: unsafe fn (*mut (dyn Future<Output = T> + 'a)))
-> unsafe fn(*mut (dyn Future<Output = T> + 'static))
{
mem::transmute(ptr)
}

impl<'a, T> LocalFutureObj<'a, T> {
/// Create a `LocalFutureObj` from a custom trait object representation.
#[inline]
pub fn new<F: UnsafeFutureObj<'a, T> + '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,
}
}
Expand Down Expand Up @@ -61,17 +73,17 @@ impl<T> Future for LocalFutureObj<'_, T> {
type Output = T;

#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
unsafe {
((*self).poll_fn)((*self).ptr, cx)
Pin::new_unchecked(&mut *self.future).poll(cx)
}
}
}

impl<T> Drop for LocalFutureObj<'_, T> {
fn drop(&mut self) {
unsafe {
(self.drop_fn)(self.ptr)
(self.drop_fn)(self.future)
}
}
}
Expand Down Expand Up @@ -119,115 +131,174 @@ impl<T> 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<T>;
/// ## Implementor
///
/// The trait implementor must guarantee that it is safe to convert the
/// provided `*mut (dyn Future<Output = T> + 'a)` into a `Pin<&mut (dyn
/// Future<Output = T> + 'a)>` and call methods on it, non-reentrantly,
/// until `UnsafeFutureObj::drop` is called with it.
fn into_raw(self) -> *mut (dyn Future<Output = T> + '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<Output
/// = T> + 'a)>`
/// * the pointer must not be used again after this function is called
unsafe fn drop(ptr: *mut (dyn Future<Output = T> + 'a));
}

unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for &'a mut F
where
F: Future<Output = T> + Unpin + 'a
{
fn into_raw(self) -> *mut () {
self as *mut F as *mut ()
fn into_raw(self) -> *mut (dyn Future<Output = T> + 'a) {
self as *mut dyn Future<Output = T>
}

unsafe fn poll(ptr: *mut (), cx: &mut Context<'_>) -> Poll<T> {
let p: Pin<&mut F> = Pin::new_unchecked(&mut *(ptr as *mut F));
F::poll(p, cx)
unsafe fn drop(_ptr: *mut (dyn Future<Output = T> + 'a)) {}
}

unsafe impl<'a, T> UnsafeFutureObj<'a, T> for &'a mut (dyn Future<Output = T> + Unpin + 'a)
{
fn into_raw(self) -> *mut (dyn Future<Output = T> + 'a) {
self as *mut dyn Future<Output = T>
}

unsafe fn drop(_ptr: *mut ()) {}
unsafe fn drop(_ptr: *mut (dyn Future<Output = T> + 'a)) {}
}

unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Pin<&'a mut F>
where
F: Future<Output = T> + '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<Output = T> + 'a) {
unsafe { self.get_unchecked_mut() as *mut dyn Future<Output = T> }
}

unsafe fn poll(ptr: *mut (), cx: &mut Context<'_>) -> Poll<T> {
let future: Pin<&mut F> = Pin::new_unchecked(&mut *(ptr as *mut F));
F::poll(future, cx)
unsafe fn drop(_ptr: *mut (dyn Future<Output = T> + 'a)) {}
}

unsafe impl<'a, T> UnsafeFutureObj<'a, T> for Pin<&'a mut (dyn Future<Output = T> + 'a)>
{
fn into_raw(self) -> *mut (dyn Future<Output = T> + 'a) {
unsafe { self.get_unchecked_mut() as *mut dyn Future<Output = T> }
}

unsafe fn drop(_ptr: *mut ()) {}
unsafe fn drop(_ptr: *mut (dyn Future<Output = T> + '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<F>
where F: Future<Output = T> + 'a
{
fn into_raw(self) -> *mut () {
Box::into_raw(self) as *mut ()
fn into_raw(self) -> *mut (dyn Future<Output = T> + 'a) {
Box::into_raw(self)
}

unsafe fn poll(ptr: *mut (), cx: &mut Context<'_>) -> Poll<T> {
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<Output = T> + '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<dyn Future<Output = T> + 'a> {
fn into_raw(self) -> *mut (dyn Future<Output = T> + 'a) {
Box::into_raw(self)
}

unsafe fn drop(ptr: *mut (dyn Future<Output = T> + 'a)) {
drop(Box::from_raw(ptr))
}
}

unsafe impl<'a, T: 'a> UnsafeFutureObj<'a, T> for Box<dyn Future<Output = T> + Send + 'a> {
fn into_raw(self) -> *mut (dyn Future<Output = T> + 'a) {
Box::into_raw(self)
}

unsafe fn drop(ptr: *mut (dyn Future<Output = T> + 'a)) {
drop(Box::from_raw(ptr))
}
}

unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Pin<Box<F>>
where
F: Future<Output = T> + '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
fn into_raw(mut self) -> *mut (dyn Future<Output = T> + 'a) {
let ptr = unsafe { self.as_mut().get_unchecked_mut() as *mut _ };
mem::forget(self);
ptr
}

unsafe fn drop(ptr: *mut (dyn Future<Output = T> + 'a)) {
drop(Pin::from(Box::from_raw(ptr)))
}
}

unsafe impl<'a, T: 'a> UnsafeFutureObj<'a, T> for Pin<Box<dyn Future<Output = T> + 'a>> {
fn into_raw(mut self) -> *mut (dyn Future<Output = T> + 'a) {
let ptr = unsafe { self.as_mut().get_unchecked_mut() as *mut _ };
mem::forget(self);
ptr
}

unsafe fn drop(ptr: *mut (dyn Future<Output = T> + 'a)) {
drop(Pin::from(Box::from_raw(ptr)))
}
}

unsafe impl<'a, T: 'a> UnsafeFutureObj<'a, T> for Pin<Box<dyn Future<Output = T> + Send + 'a>> {
fn into_raw(mut self) -> *mut (dyn Future<Output = T> + 'a) {
let ptr = unsafe { self.as_mut().get_unchecked_mut() as *mut _ };
mem::forget(self);
ptr
}

unsafe fn poll(ptr: *mut (), cx: &mut Context<'_>) -> Poll<T> {
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<Output = T> + 'a)) {
drop(Pin::from(Box::from_raw(ptr)))
}
}

impl<'a, F: Future<Output = ()> + Send + 'a> From<Box<F>> for FutureObj<'a, ()> {
fn from(boxed: Box<F>) -> Self {
FutureObj::new(boxed)
}
}

unsafe fn drop(ptr: *mut ()) {
#[allow(clippy::cast_ptr_alignment)]
drop(Pin::from(Box::from_raw(ptr as *mut F)));
impl<'a> From<Box<dyn Future<Output = ()> + Send + 'a>> for FutureObj<'a, ()> {
fn from(boxed: Box<dyn Future<Output = ()> + Send + 'a>) -> Self {
FutureObj::new(boxed)
}
}

Expand All @@ -237,20 +308,32 @@ mod if_alloc {
}
}

impl<'a, F: Future<Output = ()> + Send + 'a> From<Box<F>> for FutureObj<'a, ()> {
fn from(boxed: Box<F>) -> Self {
impl<'a> From<Pin<Box<dyn Future<Output = ()> + Send + 'a>>> for FutureObj<'a, ()> {
fn from(boxed: Pin<Box<dyn Future<Output = ()> + Send + 'a>>) -> Self {
FutureObj::new(boxed)
}
}

impl<'a, F: Future<Output = ()> + 'a> From<Box<F>> for LocalFutureObj<'a, ()> {
fn from(boxed: Box<F>) -> Self {
LocalFutureObj::new(boxed)
}
}

impl<'a> From<Box<dyn Future<Output = ()> + 'a>> for LocalFutureObj<'a, ()> {
fn from(boxed: Box<dyn Future<Output = ()> + 'a>) -> Self {
LocalFutureObj::new(boxed)
}
}

impl<'a, F: Future<Output = ()> + 'a> From<Pin<Box<F>>> for LocalFutureObj<'a, ()> {
fn from(boxed: Pin<Box<F>>) -> Self {
LocalFutureObj::new(boxed)
}
}

impl<'a, F: Future<Output = ()> + 'a> From<Box<F>> for LocalFutureObj<'a, ()> {
fn from(boxed: Box<F>) -> Self {
impl<'a> From<Pin<Box<dyn Future<Output = ()> + 'a>>> for LocalFutureObj<'a, ()> {
fn from(boxed: Pin<Box<dyn Future<Output = ()> + 'a>>) -> Self {
LocalFutureObj::new(boxed)
}
}
Expand Down
4 changes: 2 additions & 2 deletions futures-util/src/future/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
6 changes: 3 additions & 3 deletions futures/tests/future_obj.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#![feature(async_await)]

use futures::future::{Future, FutureObj};
use futures::future::{Future, FutureObj, FutureExt};
use std::pin::Pin;
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]
Expand All @@ -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);
}