use std::cell::Cell;
use std::sync::OnceLock;
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::Dispatcher;
static GLOBAL_DISPATCHER: OnceLock<Dispatcher> = OnceLock::new();
static LOCAL_COUNT: AtomicUsize = AtomicUsize::new(0);
thread_local! {
static LOCAL_DISPATCHER: Cell<Option<Dispatcher>> = const { Cell::new(None) };
}
pub fn set_global_dispatcher(dispatcher: Dispatcher) -> Result<(), Dispatcher> {
let mut result = Err(dispatcher);
{
let result = &mut result;
let _ = GLOBAL_DISPATCHER.get_or_init(|| std::mem::replace(result, Ok(())).unwrap_err());
}
result
}
pub struct LocalDispatcherGuard {
previous: Option<Dispatcher>,
}
impl LocalDispatcherGuard {
pub fn take(self) -> Dispatcher {
let dispatcher = LOCAL_DISPATCHER.take();
drop(self); dispatcher.unwrap()
}
}
impl Drop for LocalDispatcherGuard {
fn drop(&mut self) {
let previous = self.previous.take();
if previous.is_none() {
LOCAL_COUNT.fetch_sub(1, Ordering::Relaxed);
}
LOCAL_DISPATCHER.set(previous);
}
}
pub fn set_local_dispatcher(dispatcher: Dispatcher) -> LocalDispatcherGuard {
let previous = LOCAL_DISPATCHER.replace(Some(dispatcher));
if previous.is_none() {
LOCAL_COUNT.fetch_add(1, Ordering::Relaxed);
}
LocalDispatcherGuard { previous }
}
pub fn with_dispatcher<F, R>(f: F) -> R
where
F: FnOnce(&Dispatcher) -> R,
R: Default,
{
if LOCAL_COUNT.load(Ordering::Relaxed) > 0
&& let Some(dispatcher) = LOCAL_DISPATCHER.take()
{
let result = f(&dispatcher);
LOCAL_DISPATCHER.set(Some(dispatcher));
return result;
}
if let Some(dispatcher) = GLOBAL_DISPATCHER.get() {
return f(dispatcher);
}
Default::default()
}