Skip to content

Commit e2746ec

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

File tree

1 file changed

+161
-52
lines changed

1 file changed

+161
-52
lines changed

src/lib.rs

Lines changed: 161 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -75,26 +75,53 @@ 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,
8397
/// we keep a manual vtable that serves pointers to Poll::poll and Drop::drop. These are
8498
/// generated and filled in by `StackFuture::from`.
8599
///
86100
/// This field stores a pointer to the poll function wrapper.
87-
poll_fn: fn(this: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T>,
101+
///
102+
/// SAFETY:
103+
/// * the argument `this` must be the same instance of this type that `poll_fn` was obtained from.
104+
poll_fn: unsafe fn(this: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T>,
88105
/// Stores a pointer to the drop function wrapper
89106
///
90107
/// See the documentation on `poll_fn` for more details.
91-
drop_fn: fn(this: &mut Self),
108+
///
109+
/// SAFETY:
110+
/// * the argument `this` must be the same instance of this type that `drop_fn` was obtained from.
111+
/// * must only be called from the Drop impl of this type.
112+
drop_fn: unsafe fn(this: &mut Self),
92113
/// StackFuture can be used similarly to a `dyn Future`. We keep a PhantomData
93114
/// here so the type system knows this.
94-
_phantom: PhantomData<dyn Future<Output = T> + Send + 'a>,
115+
_phantom: PhantomData<dyn Future<Output = T> + 'a>,
95116
}
96117

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

153-
Self::try_from(future).unwrap()
255+
Self::try_from_inner(future).unwrap()
154256
}
155257

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>>
258+
fn try_from_inner<F>(future: F) -> Result<Self, IntoStackFutureError<F>>
165259
where
166-
F: Future<Output = T> + Send + 'a, // the bounds here should match those in the _phantom field
260+
F: Future<Output = T> + 'a, // the bounds here should match those in the _phantom field
167261
{
168262
if Self::has_space_for_val(&future) && Self::has_alignment_for_val(&future) {
169-
let mut result = StackFuture {
263+
let mut result = Self {
170264
data: [MaybeUninit::uninit(); STACK_SIZE],
265+
// SAFETY:
266+
// `poll_inner` and `drop_inner` both require `F` to match the future type
267+
// used to construct `self` here. The invariants on the `poll_fn` and `drop_fn`
268+
// fields require that they are only called using the original instance they were
269+
// obtained from, which ensures that `F` will still match at the point
270+
// they are called.
171271
poll_fn: Self::poll_inner::<F>,
272+
// SAFETY:
273+
// the invariants on `drop_fn` transitively uphold the requirement that `drop_inner`
274+
// is only called from the Drop impl of this type.
172275
drop_fn: Self::drop_inner::<F>,
173276
_phantom: PhantomData,
174277
};
@@ -192,34 +295,35 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
192295
}
193296
}
194297

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.
205298
#[cfg(feature = "alloc")]
206-
pub fn from_or_box<F>(future: F) -> Self
299+
fn from_or_box_inner<F>(future: F) -> Self
207300
where
208-
F: Future<Output = T> + Send + 'a, // the bounds here should match those in the _phantom field
301+
F: Future<Output = T> + 'a, // the bounds here should match those in the _phantom field
209302
{
210-
Self::try_from(future).unwrap_or_else(|err| Self::from(Box::pin(err.into_inner())))
303+
Self::try_from_inner(future).unwrap_or_else(|err| Self::from_inner(Box::pin(err.into_inner())))
211304
}
212305

213306
/// A wrapper around the inner future's poll function, which we store in the poll_fn field
214307
/// of this struct.
215-
fn poll_inner<F: Future>(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F::Output> {
216-
self.as_pin_mut_ref::<F>().poll(cx)
308+
///
309+
/// SAFETY:
310+
/// * the generic argument `F` must be the exact same type originally used to construct
311+
/// this instance via `try_from_inner`.
312+
unsafe fn poll_inner<F: Future>(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F::Output> {
313+
unsafe { self.as_pin_mut_ref::<F>() }.poll(cx)
217314
}
218315

219316
/// A wrapper around the inner future's drop function, which we store in the drop_fn field
220317
/// of this struct.
221-
fn drop_inner<F>(&mut self) {
222-
// SAFETY: *this.as_mut_ptr() was previously written as type F
318+
///
319+
/// SAFETY:
320+
/// * the generic argument `F` must be the exact same type originally used to construct
321+
/// this instance via `try_from_inner`.
322+
/// * must only be called from the drop impl of this type.
323+
unsafe fn drop_inner<F>(&mut self) {
324+
// SAFETY:
325+
// * this.as_mut_ptr() was previously written as type F
326+
// * caller ensures this will only be called from the drop impl of this type.
223327
unsafe { ptr::drop_in_place(self.as_mut_ptr::<F>()) }
224328
}
225329

@@ -236,8 +340,12 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
236340
}
237341

238342
/// Returns a pinned mutable reference to a type F stored in self.data
239-
fn as_pin_mut_ref<F>(self: Pin<&mut Self>) -> Pin<&mut F> {
240-
// SAFETY: `StackFuture` is only created by `StackFuture::from`, which
343+
///
344+
/// SAFETY:
345+
/// * the generic argument `F` must be the exact same type originally used to construct
346+
/// this instance via `try_from_inner`.
347+
unsafe fn as_pin_mut_ref<F>(self: Pin<&mut Self>) -> Pin<&mut F> {
348+
// SAFETY: `StackFuture` is only created by `StackFuture::try_from_inner`, which
241349
// writes an `F` to `self.as_mut_ptr(), so it's okay to cast the `*mut F`
242350
// to an `&mut F` with the same lifetime as `self`.
243351
//
@@ -277,26 +385,27 @@ impl<'a, T, const STACK_SIZE: usize> StackFuture<'a, T, { STACK_SIZE }> {
277385
}
278386
}
279387

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

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-
}
391+
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
392+
// SAFETY:
393+
// `self.poll_fn`` is private and never reassigned, so this must be the same instance
394+
// that `poll_fn` was originally obtained from.
395+
unsafe { (self.poll_fn)(self, cx) }
294396
}
295397
}
296398

297-
impl<'a, T, const STACK_SIZE: usize> Drop for StackFuture<'a, T, { STACK_SIZE }> {
399+
impl<'a, T, const STACK_SIZE: usize, const SEND: bool> Drop for StackFutureImpl<'a, T, { STACK_SIZE }, SEND> {
298400
fn drop(&mut self) {
299-
(self.drop_fn)(self);
401+
// SAFETY:
402+
// * `self.drop_fn`` is private and never reassigned, so this must be the same instance
403+
// that `drop_fn` was originally obtained from.
404+
// * we are calling this from the drop impl of this type, which is the only valid place
405+
// to call `drop_fn`.
406+
unsafe {
407+
(self.drop_fn)(self);
408+
}
300409
}
301410
}
302411

0 commit comments

Comments
 (0)