Skip to content

Commit

Permalink
Move ManuallyDrop to a separate structure
Browse files Browse the repository at this point in the history
  • Loading branch information
yoshuawuyts committed Jun 12, 2023
1 parent 0d27cd8 commit 0bd7d99
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 19 deletions.
22 changes: 13 additions & 9 deletions src/future/join/array.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::Join as JoinTrait;
use crate::utils::{self, array_to_manually_drop, PollArray, WakerArray};
use crate::utils::{self, FutureArray, PollArray, WakerArray};

use core::array;
use core::fmt;
Expand All @@ -24,13 +24,20 @@ pub struct Join<Fut, const N: usize>
where
Fut: Future,
{
/// A boolean which holds whether the future has completed
consumed: bool,
/// The number of futures which are currently still in-flight
pending: usize,
/// The output data, to be returned after the future completes
items: [MaybeUninit<<Fut as Future>::Output>; N],
/// A structure holding the waker passed to the future, and the various
/// sub-wakers passed to the contained futures.
wakers: WakerArray<N>,
/// The individual poll state of each future.
state: PollArray<N>,
#[pin]
futures: [ManuallyDrop<Fut>; N],
/// The array of futures passed to the structure.
futures: FutureArray<Fut, N>,
}

impl<Fut, const N: usize> Join<Fut, N>
Expand All @@ -45,7 +52,7 @@ where
items: array::from_fn(|_| MaybeUninit::uninit()),
wakers: WakerArray::new(),
state: PollArray::new(),
futures: array_to_manually_drop(futures),
futures: FutureArray::new(futures),
}
}
}
Expand Down Expand Up @@ -80,7 +87,7 @@ where

#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
let this = self.project();

assert!(
!*this.consumed,
Expand All @@ -95,7 +102,7 @@ where
}

// Poll all ready futures
for (i, mut fut) in utils::iter_pin_mut(this.futures.as_mut()).enumerate() {
for (i, mut fut) in this.futures.iter().enumerate() {
if this.state[i].is_pending() && readiness.clear_ready(i) {
// unlock readiness so we don't deadlock when polling
drop(readiness);
Expand Down Expand Up @@ -184,10 +191,7 @@ where
for i in indexes {
// SAFETY: we've just filtered down to *only* the pending futures,
// which have not yet been dropped.
unsafe {
let futures = this.futures.as_mut().get_unchecked_mut();
ManuallyDrop::drop(&mut futures[i]);
};
unsafe { this.futures.as_mut().drop(i) };
}
}
}
Expand Down
10 changes: 1 addition & 9 deletions src/utils/array.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::mem::{self, ManuallyDrop, MaybeUninit};
use std::mem::{self, MaybeUninit};

/// Extracts the values from an array of `MaybeUninit` containers.
///
Expand All @@ -20,11 +20,3 @@ pub(crate) unsafe fn array_assume_init<T, const N: usize>(array: [MaybeUninit<T>
mem::forget(array);
ret
}

/// Cast an array of `T` to an array of `MaybeUninit<T>`
pub(crate) fn array_to_manually_drop<T, const N: usize>(arr: [T; N]) -> [ManuallyDrop<T>; N] {
// Implementation copied from: https://doc.rust-lang.org/src/core/mem/maybe_uninit.rs.html#1292
let arr = MaybeUninit::new(arr);
// SAFETY: T and MaybeUninit<T> have the same layout
unsafe { mem::transmute_copy(&mem::ManuallyDrop::new(arr)) }
}
44 changes: 44 additions & 0 deletions src/utils/futures/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::{
mem::{self, ManuallyDrop, MaybeUninit},
pin::Pin,
};

/// An array of futures which can be dropped in-place, intended to be
/// constructed once and then accessed through pin projections.
pub(crate) struct FutureArray<T, const N: usize> {
futures: [ManuallyDrop<T>; N],
}

impl<T, const N: usize> FutureArray<T, N> {
/// Create a new instance of `FutureArray`
pub(crate) fn new(futures: [T; N]) -> Self {
// Implementation copied from: https://doc.rust-lang.org/src/core/mem/maybe_uninit.rs.html#1292
let futures = MaybeUninit::new(futures);
// SAFETY: T and MaybeUninit<T> have the same layout
let futures = unsafe { mem::transmute_copy(&mem::ManuallyDrop::new(futures)) };
Self { futures }
}

/// Create an iterator of pinned references.
pub(crate) fn iter(self: Pin<&mut Self>) -> impl Iterator<Item = Pin<&mut ManuallyDrop<T>>> {
// SAFETY: `std` _could_ make this unsound if it were to decide Pin's
// invariants aren't required to transmit through slices. Otherwise this has
// the same safety as a normal field pin projection.
unsafe { self.get_unchecked_mut() }
.futures
.iter_mut()
.map(|t| unsafe { Pin::new_unchecked(t) })
}

/// Drop a future at the given index.
///
/// # Safety
///
/// The future is held in a `ManuallyDrop`, so no double-dropping, etc
pub(crate) unsafe fn drop(mut self: Pin<&mut Self>, idx: usize) {
unsafe {
let futures = self.as_mut().get_unchecked_mut().futures.as_mut();
ManuallyDrop::drop(&mut futures[idx]);
};
}
}
3 changes: 3 additions & 0 deletions src/utils/futures/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod array;

pub(crate) use array::FutureArray;
4 changes: 3 additions & 1 deletion src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Utilities to implement the different futures of this crate.
mod array;
mod futures;
mod indexer;
mod pin;
mod poll_state;
mod tuple;
mod wakers;

pub(crate) use array::{array_assume_init, array_to_manually_drop};
pub(crate) use self::futures::FutureArray;
pub(crate) use array::array_assume_init;
pub(crate) use indexer::Indexer;
pub(crate) use pin::{get_pin_mut, get_pin_mut_from_vec, iter_pin_mut, iter_pin_mut_vec};
pub(crate) use poll_state::MaybeDone;
Expand Down

0 comments on commit 0bd7d99

Please sign in to comment.