use std::{
alloc::Layout,
fmt,
future::{self, Future},
mem,
pin::Pin,
ptr,
task::{Context, Poll},
};
use futures_util::future::{BoxFuture, LocalBoxFuture};
use scopeguard::defer;
fn try_reuse_store<T: ?Sized, U, F>(store: Pin<Box<T>>, new_val: U, cb: F) -> Result<(), U>
where
F: FnOnce(Box<U>),
{
let t_layout = Layout::for_value::<T>(&*store);
let u_layout = Layout::new::<U>();
if t_layout != u_layout {
return Err(new_val);
}
let raw: *mut T = Box::into_raw(unsafe { Pin::into_inner_unchecked(store) });
defer!({
let raw: *mut U = raw.cast::<U>();
unsafe { raw.write(new_val) };
let store = unsafe { Box::from_raw(raw) };
cb(store)
});
unsafe { ptr::drop_in_place(raw) };
Ok(())
}
pub mod sync {
use super::*;
pub struct BoxedFutureStore<'a, T> {
store: BoxFuture<'a, T>,
}
impl<'a, T: 'a> BoxedFutureStore<'a, T> {
pub fn new<F>(fut: F) -> Self
where
F: Future<Output = T> + Send + 'a,
{
Self {
store: Box::pin(fut),
}
}
pub fn set<F>(&mut self, fut: F)
where
F: Future<Output = T> + Send + 'a,
{
let store = mem::replace(&mut self.store, Box::pin(future::pending()));
if let Err(fut) = try_reuse_store(store, fut, |b| self.store = Pin::from(b)) {
*self = Self::new(fut);
}
}
pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll<T> {
self.store.as_mut().poll(cx)
}
}
impl<T> Future for BoxedFutureStore<'_, T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
Pin::into_inner(self).store.as_mut().poll(cx)
}
}
unsafe impl<T> Sync for BoxedFutureStore<'_, T> {}
impl<T> fmt::Debug for BoxedFutureStore<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BoxedFutureStore").finish()
}
}
}
pub mod unsync {
use super::*;
pub struct LocalBoxedFutureStore<'a, T> {
store: LocalBoxFuture<'a, T>,
}
impl<'a, T: 'a> LocalBoxedFutureStore<'a, T> {
pub fn new<F>(fut: F) -> Self
where
F: Future<Output = T> + 'a,
{
Self {
store: Box::pin(fut),
}
}
pub fn set<F>(&mut self, fut: F)
where
F: Future<Output = T> + 'a,
{
let store = mem::replace(&mut self.store, Box::pin(future::pending()));
if let Err(fut) = try_reuse_store(store, fut, |b| self.store = Pin::from(b)) {
*self = Self::new(fut);
}
}
pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll<T> {
self.store.as_mut().poll(cx)
}
}
impl<T> Future for LocalBoxedFutureStore<'_, T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
Pin::into_inner(self).store.as_mut().poll(cx)
}
}
impl<T> fmt::Debug for LocalBoxedFutureStore<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LocalBoxedFutureStore").finish()
}
}
}