task_local context over context objects

- 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
This commit is contained in:
2026-01-01 14:54:29 +00:00
parent 06debb3636
commit 32d6237dc5
92 changed files with 2507 additions and 2223 deletions

View File

@@ -1,16 +1,21 @@
//! A pattern for running async code from sync destructors and other
//! unfortunately sync callbacks
//!
//! We create a task_local
//! 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 some_executor::task_local;
use task_local::task_local;
#[derive(Default)]
struct StashedFutures {
queue: VecDeque<Pin<Box<dyn Future<Output = ()>>>>,
queue: RefCell<VecDeque<Pin<Box<dyn Future<Output = ()>>>>>,
}
task_local! {
@@ -23,7 +28,7 @@ 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_mut(|sf| sf.unwrap().queue.pop_front()) {
while let Some(fut) = STASHED_FUTURES.with(|sf| sf.queue.borrow_mut().pop_front()) {
fut.await;
}
val
@@ -33,10 +38,7 @@ pub async fn with_stash<F: Future>(fut: F) -> F::Output {
/// 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 + 'static>(fut: F) {
STASHED_FUTURES.with_mut(|sf| {
sf.expect("No stash! Timely completion cannot be guaranteed").queue.push_back(Box::pin(async {
fut.await;
}))
})
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")
}