use std::cell::RefCell;
use crate::scope::{ContextStack, ScopeGuard};
use crate::value::ContextValue;
thread_local! {
pub(crate) static CONTEXT: RefCell<ContextStack> = RefCell::new(ContextStack::new());
static FORCE_THREAD_LOCAL_DEPTH: std::cell::Cell<u32> = const { std::cell::Cell::new(0) };
}
#[cfg(feature = "tokio")]
tokio::task_local! {
pub(crate) static TASK_CONTEXT: RefCell<ContextStack>;
}
pub(crate) fn with_current_stack<R>(mut f: impl FnMut(&RefCell<ContextStack>) -> R) -> R {
#[cfg(feature = "tokio")]
{
let force = FORCE_THREAD_LOCAL_DEPTH.with(|c| c.get()) > 0;
if !force {
let result: std::cell::Cell<Option<R>> = std::cell::Cell::new(None);
let found = TASK_CONTEXT.try_with(|stack| {
result.set(Some(f(stack)));
});
if found.is_ok() {
return result.into_inner()
.expect("invariant: closure set the result when try_with succeeded");
}
}
}
CONTEXT.with(|stack| f(stack))
}
pub fn enter_scope() -> ScopeGuard {
with_current_stack(|cell| {
let mut stack = cell.borrow_mut();
let (id, depth) = stack.push_scope();
ScopeGuard::new(id, depth)
})
}
pub fn enter_named_scope(name: impl Into<String>) -> ScopeGuard {
let name = name.into();
with_current_stack(|cell| {
let mut stack = cell.borrow_mut();
let (id, depth) = stack.push_named_scope(name.clone());
ScopeGuard::new(id, depth)
})
}
pub(crate) fn leave_scope(expected_depth: usize) {
with_current_stack(|cell| {
let mut stack = cell.borrow_mut();
stack.pop_scope(expected_depth);
})
}
pub fn scope<R>(f: impl FnOnce() -> R) -> R {
let _guard = enter_scope();
f()
}
#[cfg(feature = "tokio")]
pub async fn scope_async<F, R>(f: F) -> R
where
F: std::future::Future<Output = R>,
{
let depth = with_current_stack(|cell| {
let mut stack = cell.borrow_mut();
let (_id, depth) = stack.push_scope();
depth
});
let result = f.await;
with_current_stack(|cell| {
let mut stack = cell.borrow_mut();
stack.pop_scope(depth);
});
result
}
#[cfg(feature = "tokio")]
pub async fn named_scope_async<F, R>(name: impl Into<String>, f: F) -> R
where
F: std::future::Future<Output = R>,
{
let name = name.into();
let depth = with_current_stack(|cell| {
let mut stack = cell.borrow_mut();
let (_id, depth) = stack.push_named_scope(name.clone());
depth
});
let result = f.await;
with_current_stack(|cell| {
let mut stack = cell.borrow_mut();
stack.pop_scope(depth);
});
result
}
pub(crate) fn get_value(key: &str) -> Option<Box<dyn ContextValue>> {
with_current_stack(|cell| {
let stack = cell.borrow();
stack.lookup(key).map(|v| v.clone_boxed())
})
}
pub(crate) fn set_value(key: &'static str, value: Box<dyn ContextValue>) {
let mut value = Some(value);
let _old = with_current_stack(|cell| {
let mut stack = cell.borrow_mut();
stack.set(key, value.take().expect("invariant: value is always Some on entry"))
});
}
pub(crate) fn collect_values() -> std::collections::HashMap<&'static str, Box<dyn ContextValue>> {
with_current_stack(|cell| {
let stack = cell.borrow();
stack
.merged_values()
.into_iter()
.map(|(k, v)| (k, v.clone_boxed()))
.collect()
})
}
pub fn scope_chain() -> Vec<String> {
with_current_stack(|cell| {
let stack = cell.borrow();
stack.scope_chain()
})
}
pub(crate) fn collect_scope_chain() -> Vec<String> {
scope_chain()
}
pub(crate) fn set_remote_chain(chain: Vec<String>) {
with_current_stack(|cell| {
let mut stack = cell.borrow_mut();
stack.set_remote_chain(chain.clone());
})
}
pub fn force_thread_local<R>(f: impl FnOnce() -> R) -> R {
FORCE_THREAD_LOCAL_DEPTH.with(|c| c.set(c.get() + 1));
struct DepthGuard;
impl Drop for DepthGuard {
fn drop(&mut self) {
crate::storage::FORCE_THREAD_LOCAL_DEPTH.with(|c| c.set(c.get() - 1));
}
}
let _guard = DepthGuard;
f()
}