- interner impls logically separate from API in orchid-base (default host interner still in base for testing) - error reporting, logging, and a variety of other features passed down via context in extension, not yet in host to maintain library-ish profile, should consider options - no global spawn mechanic, the host has a spawn function but extensions only get a stash for enqueuing async work in sync callbacks which is then explicitly, manually, and with strict order popped and awaited - still deadlocks nondeterministically for some ungodly reason
45 lines
1.5 KiB
Rust
45 lines
1.5 KiB
Rust
//! 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<VecDeque<Pin<Box<dyn Future<Output = ()>>>>>,
|
|
}
|
|
|
|
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<F: Future>(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<F: Future<Output = ()> + '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")
|
|
}
|