use std::alloc::Layout; use std::fmt; use std::future::{self, Future}; use std::mem::{self, ManuallyDrop}; use std::pin::Pin; use std::ptr; use std::task::{Context, Poll}; /// A reusable `Pin + Send + 'a>>`. /// /// This type lets you replace the future stored in the box without /// reallocating when the size and alignment permits this. pub struct ReusableBoxFuture<'a, T> { boxed: Pin + Send + 'a>>, } impl<'a, T> ReusableBoxFuture<'a, T> { /// Create a new `ReusableBoxFuture` containing the provided future. pub fn new(future: F) -> Self where F: Future + Send + 'a, { Self { boxed: Box::pin(future), } } /// Replace the future currently stored in this box. /// /// This reallocates if and only if the layout of the provided future is /// different from the layout of the currently stored future. pub fn set(&mut self, future: F) where F: Future + Send + 'a, { if let Err(future) = self.try_set(future) { *self = Self::new(future); } } /// Replace the future currently stored in this box. /// /// This function never reallocates, but returns an error if the provided /// future has a different size or alignment from the currently stored /// future. pub fn try_set(&mut self, future: F) -> Result<(), F> where F: Future + Send + 'a, { // If we try to inline the contents of this function, the type checker complains because // the bound `T: 'a` is not satisfied in the call to `pending()`. But by putting it in an // inner function that doesn't have `T` as a generic parameter, we implicitly get the bound // `F::Output: 'a` transitively through `F: 'a`, allowing us to call `pending()`. #[inline(always)] fn real_try_set<'a, F>( this: &mut ReusableBoxFuture<'a, F::Output>, future: F, ) -> Result<(), F> where F: Future + Send + 'a, { // future::Pending is a ZST so this never allocates. let boxed = mem::replace(&mut this.boxed, Box::pin(future::pending())); reuse_pin_box(boxed, future, |boxed| this.boxed = Pin::from(boxed)) } real_try_set(self, future) } /// Get a pinned reference to the underlying future. pub fn get_pin(&mut self) -> Pin<&mut (dyn Future + Send)> { self.boxed.as_mut() } /// Poll the future stored inside this box. pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll { self.get_pin().poll(cx) } } impl Future for ReusableBoxFuture<'_, T> { type Output = T; /// Poll the future stored inside this box. fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::into_inner(self).get_pin().poll(cx) } } // The only method called on self.boxed is poll, which takes &mut self, so this // struct being Sync does not permit any invalid access to the Future, even if // the future is not Sync. unsafe impl Sync for ReusableBoxFuture<'_, T> {} impl fmt::Debug for ReusableBoxFuture<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ReusableBoxFuture").finish() } } fn reuse_pin_box(boxed: Pin>, new_value: U, callback: F) -> Result where F: FnOnce(Box) -> O, { let layout = Layout::for_value::(&*boxed); if layout != Layout::new::() { return Err(new_value); } // SAFETY: We don't ever construct a non-pinned reference to the old `T` from now on, and we // always drop the `T`. let raw: *mut T = Box::into_raw(unsafe { Pin::into_inner_unchecked(boxed) }); // When dropping the old value panics, we still want to call `callback` — so move the rest of // the code into a guard type. let guard = CallOnDrop::new(|| { let raw: *mut U = raw.cast::(); unsafe { raw.write(new_value) }; // SAFETY: // - `T` and `U` have the same layout. // - `raw` comes from a `Box` that uses the same allocator as this one. // - `raw` points to a valid instance of `U` (we just wrote it in). let boxed = unsafe { Box::from_raw(raw) }; callback(boxed) }); // Drop the old value. unsafe { ptr::drop_in_place(raw) }; // Run the rest of the code. Ok(guard.call()) } struct CallOnDrop O> { f: ManuallyDrop, } impl O> CallOnDrop { fn new(f: F) -> Self { let f = ManuallyDrop::new(f); Self { f } } fn call(self) -> O { let mut this = ManuallyDrop::new(self); let f = unsafe { ManuallyDrop::take(&mut this.f) }; f() } } impl O> Drop for CallOnDrop { fn drop(&mut self) { let f = unsafe { ManuallyDrop::take(&mut self.f) }; f(); } }