Skip to content

Commit 02e32d4

Browse files
committed
feat: support non-Send stackfutures
1 parent f76acb7 commit 02e32d4

1 file changed

Lines changed: 131 additions & 51 deletions

File tree

src/lib.rs

Lines changed: 131 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,22 @@ use alloc::boxed::Box;
7575
/// it also respects any alignment requirements for the wrapped future. Note that the
7676
/// wrapped future's alignment must be less than or equal to that of the overall
7777
/// `StackFuture` struct.
78+
// NOTE: we use a type alias rather than a default const generic argument, as that would make methods
79+
// like StackFuture::new ambiguous when calling.
80+
pub type StackFuture<'a, T, const STACK_SIZE: usize> = StackFutureImpl<'a, T, STACK_SIZE, true>;
81+
82+
/// A variant of [`StackFuture`] which allows for futures that do not implement the [`Send`] trait.
83+
///
84+
/// See the documentation of `StackFuture` for more information.
85+
pub type LocalStackFuture<'a, T, const STACK_SIZE: usize> = StackFutureImpl<'a, T, STACK_SIZE, false>;
86+
87+
/// A variant of [`StackFuture`] which supports either [`Send`] ofr non-`Send` futures, depending
88+
/// on the value of the `SEND` const generic argument.
89+
///
90+
/// In most cases, you will want to use `StackFuture` or [`LocalStackFuture`] directly.
91+
/// See the documentation for [`StackFuture`] for more details.
7892
#[repr(C)] // Ensures the data first does not have any padding before it in the struct
79-
pub struct StackFuture<'a, T, const STACK_SIZE: usize> {
93+
pub struct StackFutureImpl<'a, T, const STACK_SIZE: usize, const SEND: bool> {
8094
/// An array of bytes that is used to store the wrapped future.
8195
data: [MaybeUninit<u8>; STACK_SIZE],
8296
/// Since the type of `StackFuture` does not know the underlying future that it is wrapping,
@@ -88,13 +102,22 @@ pub struct StackFuture<'a, T, const STACK_SIZE: usize> {
88102
/// Stores a pointer to the drop function wrapper
89103
///
90104
/// See the documentation on `poll_fn` for more details.
91-
drop_fn: fn(this: &mut Self),
105+
///
106+
/// SAFETY:
107+
/// Must only be called from within the Drop impl of this type.
108+
drop_fn: unsafe fn(this: &mut Self),
92109
/// StackFuture can be used similarly to a `dyn Future`. We keep a PhantomData
93110
/// here so the type system knows this.
94-
_phantom: PhantomData<dyn Future<Output = T> + Send + 'a>,
111+
_phantom: PhantomData<dyn Future<Output = T> + 'a>,
95112
}
96113

97-
impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
114+
// SAFETY:
115+
// We ensure by the API exposed for this type that the contained future will always be Send
116+
// as long as the `SEND` const generic arg is true.
117+
unsafe impl<'a, T, const STACK_SIZE: usize> Send for StackFutureImpl<'a, T, STACK_SIZE, true>
118+
{ }
119+
120+
impl<'a, T, const STACK_SIZE: usize> StackFutureImpl<'a, T, { STACK_SIZE }, true> {
98121
/// Creates a `StackFuture` from an existing future
99122
///
100123
/// See the documentation on [`StackFuture`] for examples of how to use this.
@@ -135,13 +158,88 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
135158
/// ```
136159
pub fn from<F>(future: F) -> Self
137160
where
138-
F: Future<Output = T> + Send + 'a, // the bounds here should match those in the _phantom field
161+
F: Future<Output = T> + Send + 'a,
162+
{
163+
Self::from_inner(future)
164+
}
165+
166+
/// Attempts to create a `StackFuture` from an existing future
167+
///
168+
/// If the `StackFuture` is not large enough to hold `future`, this function returns an
169+
/// `Err` with the argument `future` returned to you.
170+
///
171+
/// Panics
172+
///
173+
/// If we cannot satisfy the alignment requirements for `F`, this function will panic.
174+
pub fn try_from<F>(future: F) -> Result<Self, IntoStackFutureError<F>>
175+
where
176+
F: Future<Output = T> + Send + 'a,
177+
{
178+
Self::try_from_inner(future)
179+
}
180+
181+
/// Creates a StackFuture from the given future, boxing if necessary
182+
///
183+
/// This version will succeed even if the future is larger than `STACK_SIZE`. If the future
184+
/// is too large, `from_or_box` will allocate a `Box` on the heap and store the resulting
185+
/// boxed future in the `StackFuture`.
186+
///
187+
/// The same thing also happens if the wrapped future's alignment is larger than StackFuture's
188+
/// alignment.
189+
///
190+
/// This function requires the "alloc" crate feature.
191+
#[cfg(feature = "alloc")]
192+
pub fn from_or_box<F>(future: F) -> Self
193+
where
194+
F: Future<Output = T> + Send + 'a,
195+
{
196+
Self::from_or_box_inner(future)
197+
}
198+
}
199+
200+
impl<'a, T, const STACK_SIZE: usize> StackFutureImpl<'a, T, STACK_SIZE, false> {
201+
/// Creates a `StackFuture` from an existing future.
202+
///
203+
/// See the documentation of [`StackFuture::from`] for more details.
204+
pub fn from<F>(future: F) -> Self
205+
where
206+
F: Future<Output = T> + 'a, // the bounds here should match those in the _phantom field
207+
{
208+
Self::from_inner(future)
209+
}
210+
211+
/// Attempts to create a `StackFuture` from an existing future.
212+
///
213+
/// See the documentation of [`StackFuture::try_from`] for more details.
214+
pub fn try_from<F>(future: F) -> Result<Self, IntoStackFutureError<F>>
215+
where
216+
F: Future<Output = T> + 'a, // the bounds here should match those in the _phantom field
217+
{
218+
Self::try_from_inner(future)
219+
}
220+
221+
/// Creates a StackFuture from the given future, boxing if necessary
222+
///
223+
/// See the documentation of [`StackFuture::from_or_box`] for more details.
224+
#[cfg(feature = "alloc")]
225+
pub fn from_or_box<F>(future: F) -> Self
226+
where
227+
F: Future<Output = T> + 'a, // the bounds here should match those in the _phantom field
228+
{
229+
Self::from_or_box_inner(future)
230+
}
231+
}
232+
233+
impl<'a, T, const STACK_SIZE: usize, const SEND: bool> StackFutureImpl<'a, T, STACK_SIZE, SEND> {
234+
fn from_inner<F>(future: F) -> Self
235+
where
236+
F: Future<Output = T> + 'a, // the bounds here should match those in the _phantom field
139237
{
140238
// Ideally we would provide this as:
141239
//
142240
// impl<'a, F, const STACK_SIZE: usize> From<F> for StackFuture<'a, F::Output, { STACK_SIZE }>
143241
// where
144-
// F: Future + Send + 'a
242+
// F: Future + 'a
145243
//
146244
// However, libcore provides a blanket `impl<T> From<T> for T`, and since `StackFuture: Future`,
147245
// both impls end up being applicable to do `From<StackFuture> for StackFuture`.
@@ -150,23 +248,15 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
150248
#[allow(clippy::let_unit_value)]
151249
let _ = AssertFits::<F, STACK_SIZE>::ASSERT;
152250

153-
Self::try_from(future).unwrap()
251+
Self::try_from_inner(future).unwrap()
154252
}
155253

156-
/// Attempts to create a `StackFuture` from an existing future
157-
///
158-
/// If the `StackFuture` is not large enough to hold `future`, this function returns an
159-
/// `Err` with the argument `future` returned to you.
160-
///
161-
/// Panics
162-
///
163-
/// If we cannot satisfy the alignment requirements for `F`, this function will panic.
164-
pub fn try_from<F>(future: F) -> Result<Self, IntoStackFutureError<F>>
254+
fn try_from_inner<F>(future: F) -> Result<Self, IntoStackFutureError<F>>
165255
where
166-
F: Future<Output = T> + Send + 'a, // the bounds here should match those in the _phantom field
256+
F: Future<Output = T> + 'a, // the bounds here should match those in the _phantom field
167257
{
168258
if Self::has_space_for_val(&future) && Self::has_alignment_for_val(&future) {
169-
let mut result = StackFuture {
259+
let mut result = Self {
170260
data: [MaybeUninit::uninit(); STACK_SIZE],
171261
poll_fn: Self::poll_inner::<F>,
172262
drop_fn: Self::drop_inner::<F>,
@@ -192,22 +282,12 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
192282
}
193283
}
194284

195-
/// Creates a StackFuture from the given future, boxing if necessary
196-
///
197-
/// This version will succeed even if the future is larger than `STACK_SIZE`. If the future
198-
/// is too large, `from_or_box` will allocate a `Box` on the heap and store the resulting
199-
/// boxed future in the `StackFuture`.
200-
///
201-
/// The same thing also happens if the wrapped future's alignment is larger than StackFuture's
202-
/// alignment.
203-
///
204-
/// This function requires the "alloc" crate feature.
205285
#[cfg(feature = "alloc")]
206-
pub fn from_or_box<F>(future: F) -> Self
286+
fn from_or_box_inner<F>(future: F) -> Self
207287
where
208-
F: Future<Output = T> + Send + 'a, // the bounds here should match those in the _phantom field
288+
F: Future<Output = T> + 'a, // the bounds here should match those in the _phantom field
209289
{
210-
Self::try_from(future).unwrap_or_else(|err| Self::from(Box::pin(err.into_inner())))
290+
Self::try_from_inner(future).unwrap_or_else(|err| Self::from_inner(Box::pin(err.into_inner())))
211291
}
212292

213293
/// A wrapper around the inner future's poll function, which we store in the poll_fn field
@@ -218,8 +298,13 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
218298

219299
/// A wrapper around the inner future's drop function, which we store in the drop_fn field
220300
/// of this struct.
221-
fn drop_inner<F>(&mut self) {
222-
// SAFETY: *this.as_mut_ptr() was previously written as type F
301+
///
302+
/// SAFETY:
303+
/// Must only be called from the drop impl of this type.
304+
unsafe fn drop_inner<F>(&mut self) {
305+
// SAFETY:
306+
// * this.as_mut_ptr() was previously written as type F
307+
// * caller ensures this will only be called from the drop impl of this type.
223308
unsafe { ptr::drop_in_place(self.as_mut_ptr::<F>()) }
224309
}
225310

@@ -277,48 +362,43 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
277362
}
278363
}
279364

280-
impl<'a, T, const STACK_SIZE: usize> Future for StackFuture<'a, T, { STACK_SIZE }> {
365+
impl<'a, T, const STACK_SIZE: usize, const SEND: bool> Future for StackFutureImpl<'a, T, { STACK_SIZE }, SEND> {
281366
type Output = T;
282367

283-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
284-
// SAFETY: This is doing pin projection. We unpin self so we can
285-
// access self.poll_fn, and then re-pin self to pass it into poll_in.
286-
// The part of the struct that needs to be pinned is data, since it
287-
// contains a potentially self-referential future object, but since we
288-
// do not touch that while self is unpinned and we do not move self
289-
// while unpinned we are okay.
290-
unsafe {
291-
let this = self.get_unchecked_mut();
292-
(this.poll_fn)(Pin::new_unchecked(this), cx)
293-
}
368+
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
369+
(self.poll_fn)(self, cx)
294370
}
295371
}
296372

297-
impl<'a, T, const STACK_SIZE: usize> Drop for StackFuture<'a, T, { STACK_SIZE }> {
373+
impl<'a, T, const STACK_SIZE: usize, const SEND: bool> Drop for StackFutureImpl<'a, T, { STACK_SIZE }, SEND> {
298374
fn drop(&mut self) {
299-
(self.drop_fn)(self);
375+
// SAFETY: we are calling this from the drop impl of this type,
376+
// which is the only valid place to call `drop_fn`.
377+
unsafe {
378+
(self.drop_fn)(self);
379+
}
300380
}
301381
}
302382

303383
struct AssertFits<F, const STACK_SIZE: usize>(PhantomData<F>);
304384

305385
impl<F, const STACK_SIZE: usize> AssertFits<F, STACK_SIZE> {
306386
const ASSERT: () = {
307-
if !StackFuture::<F, STACK_SIZE>::has_space_for::<F>() {
387+
if !StackFutureImpl::<F, STACK_SIZE, false>::has_space_for::<F>() {
308388
concat_panic!(
309389
"Future is too large: ",
310-
StackFuture::<F, STACK_SIZE>::required_space::<F>(),
390+
StackFutureImpl::<F, STACK_SIZE, false>::required_space::<F>(),
311391
" > ",
312392
STACK_SIZE
313393
);
314394
}
315395

316-
if !StackFuture::<F, STACK_SIZE>::has_alignment_for::<F>() {
396+
if !StackFutureImpl::<F, STACK_SIZE, false>::has_alignment_for::<F>() {
317397
concat_panic!(
318398
"Future has incompatible alignment: ",
319399
align_of::<F>(),
320400
" > ",
321-
align_of::<StackFuture::<F, STACK_SIZE>>()
401+
align_of::<StackFutureImpl::<F, STACK_SIZE, false>>()
322402
);
323403
}
324404
};

0 commit comments

Comments
 (0)