//! A pattern for running async code from sync destructors and other //! unfortunately sync callbacks //! //! We create a task_local vecdeque which is moved into a thread_local whenever //! the task is being polled. A call to [stash] pushes the future onto this //! deque. Before [with_stash] returns, it pops everything from the deque //! individually and awaits each of them, pushing any additionally stashed //! futures onto the back of the same deque. use std::cell::RefCell; use std::collections::VecDeque; use std::pin::Pin; use task_local::task_local; #[derive(Default)] struct StashedFutures { queue: RefCell>>>>, } task_local! { static STASHED_FUTURES: StashedFutures; } /// Complete the argument future, and any futures spawned from it via [stash]. /// This is useful mostly to guarantee that messaging destructors have run. pub async fn with_stash(fut: F) -> F::Output { STASHED_FUTURES .scope(StashedFutures::default(), async { let val = fut.await; while let Some(fut) = STASHED_FUTURES.with(|sf| sf.queue.borrow_mut().pop_front()) { fut.await; } val }) .await } /// Schedule a future to be run before the next [with_stash] guard ends. This is /// most useful for sending messages from destructors. pub fn stash + 'static>(fut: F) { (STASHED_FUTURES.try_with(|sf| sf.queue.borrow_mut().push_back(Box::pin(fut)))) .expect("No stash! Timely completion cannot be guaranteed") }