From 0bd7d99d20b2865826f9bf2ee433fd57bd644a92 Mon Sep 17 00:00:00 2001 From: Yosh Date: Mon, 12 Jun 2023 02:57:52 +0200 Subject: [PATCH] Move `ManuallyDrop` to a separate structure --- src/future/join/array.rs | 22 +++++++++++-------- src/utils/array.rs | 10 +-------- src/utils/futures/array.rs | 44 ++++++++++++++++++++++++++++++++++++++ src/utils/futures/mod.rs | 3 +++ src/utils/mod.rs | 4 +++- 5 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 src/utils/futures/array.rs create mode 100644 src/utils/futures/mod.rs diff --git a/src/future/join/array.rs b/src/future/join/array.rs index d397adb..800b222 100644 --- a/src/future/join/array.rs +++ b/src/future/join/array.rs @@ -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; @@ -24,13 +24,20 @@ pub struct Join 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<::Output>; N], + /// A structure holding the waker passed to the future, and the various + /// sub-wakers passed to the contained futures. wakers: WakerArray, + /// The individual poll state of each future. state: PollArray, #[pin] - futures: [ManuallyDrop; N], + /// The array of futures passed to the structure. + futures: FutureArray, } impl Join @@ -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), } } } @@ -80,7 +87,7 @@ where #[inline] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.project(); + let this = self.project(); assert!( !*this.consumed, @@ -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); @@ -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) }; } } } diff --git a/src/utils/array.rs b/src/utils/array.rs index 35fd0b0..8eeaef5 100644 --- a/src/utils/array.rs +++ b/src/utils/array.rs @@ -1,4 +1,4 @@ -use std::mem::{self, ManuallyDrop, MaybeUninit}; +use std::mem::{self, MaybeUninit}; /// Extracts the values from an array of `MaybeUninit` containers. /// @@ -20,11 +20,3 @@ pub(crate) unsafe fn array_assume_init(array: [MaybeUninit mem::forget(array); ret } - -/// Cast an array of `T` to an array of `MaybeUninit` -pub(crate) fn array_to_manually_drop(arr: [T; N]) -> [ManuallyDrop; 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 have the same layout - unsafe { mem::transmute_copy(&mem::ManuallyDrop::new(arr)) } -} diff --git a/src/utils/futures/array.rs b/src/utils/futures/array.rs new file mode 100644 index 0000000..111afd3 --- /dev/null +++ b/src/utils/futures/array.rs @@ -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 { + futures: [ManuallyDrop; N], +} + +impl FutureArray { + /// 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 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>> { + // 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]); + }; + } +} diff --git a/src/utils/futures/mod.rs b/src/utils/futures/mod.rs new file mode 100644 index 0000000..8658e34 --- /dev/null +++ b/src/utils/futures/mod.rs @@ -0,0 +1,3 @@ +mod array; + +pub(crate) use array::FutureArray; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 33b91cf..b8c6073 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -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;