use crate::SourceMap;
use solar_data_structures::{defer, sync::Lock};
use std::sync::Arc;
scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals);
pub struct SessionGlobals {
pub(crate) symbol_interner: crate::symbol::Interner,
pub(crate) source_map: Lock<Option<Arc<SourceMap>>>,
}
impl Default for SessionGlobals {
fn default() -> Self {
Self::new()
}
}
impl SessionGlobals {
pub fn new() -> Self {
Self { symbol_interner: crate::symbol::Interner::fresh(), source_map: Lock::new(None) }
}
#[inline]
pub fn set<R>(&self, f: impl FnOnce() -> R) -> R {
if SESSION_GLOBALS.is_set() {
panic_overwrite();
}
SESSION_GLOBALS.set(self, f)
}
pub fn with_source_map<R>(source_map: Arc<SourceMap>, f: impl FnOnce() -> R) -> R {
let prev = Self::with(|g| g.source_map.lock().replace(source_map));
let _clear = defer(|| {
Self::with(|g| *g.source_map.lock() = prev);
});
f()
}
#[inline]
pub fn with<R>(f: impl FnOnce(&Self) -> R) -> R {
#[cfg(debug_assertions)]
if !SESSION_GLOBALS.is_set() {
let msg = if rayon::current_thread_index().is_some() {
"cannot access a scoped thread local variable without calling `set` first;\n\
did you forget to call `Session::enter_parallel`?"
} else {
"cannot access a scoped thread local variable without calling `set` first;\n\
did you forget to call `Session::enter`, or `Session::enter_parallel` \
if using Rayon?"
};
panic!("{msg}");
}
SESSION_GLOBALS.with(f)
}
#[inline]
pub fn with_or_default<R>(f: impl FnOnce(&Self) -> R) -> R {
if Self::is_set() {
Self::with(f)
} else {
Self::new().set(|| Self::with(f))
}
}
#[inline]
pub fn is_set() -> bool {
SESSION_GLOBALS.is_set()
}
}
#[cold]
#[inline(never)]
const fn panic_overwrite() -> ! {
panic!(
"SESSION_GLOBALS should never be overwritten! \
Use another thread if you need another SessionGlobals"
);
}