use std::{
any::{Any, TypeId},
collections::HashMap,
sync::{Arc, LazyLock, OnceLock, atomic::Ordering},
};
use sdd::{AtomicOwned, Guard, Owned, Tag};
use crate::{ServiceProvider, error::ServiceNotFoundError};
pub type Scope = HashMap<(TypeId, Option<TypeId>), std::sync::OnceLock<Arc<dyn Any + Send + Sync>>>;
fn refresh_scope_value(provider: &ServiceProvider, current: &Scope) -> Arc<Scope> {
let mut refreshed = current.clone();
let fresh = provider.create_scope();
for key in fresh.keys() {
let _ = refreshed.entry(*key).or_default();
}
Arc::new(refreshed)
}
pub(crate) fn refresh_atomic_scope(
scope: &AtomicOwned<Arc<Scope>>,
provider: &ServiceProvider,
) -> Result<Arc<Scope>, ServiceNotFoundError> {
let guard = Guard::new();
loop {
let current = scope.load(Ordering::SeqCst, &guard);
let refreshed = match current.as_ref() {
Some(current_scope) => refresh_scope_value(provider, current_scope),
None => provider.create_scope(),
};
match scope.compare_exchange(
current,
(Some(Owned::new(Arc::clone(&refreshed))), Tag::None),
Ordering::SeqCst,
Ordering::SeqCst,
&guard,
) {
Ok(_) => return Ok(refreshed),
Err(_) => continue,
}
}
}
pub trait ScopeDefault: ScopeProvider + Sized + 'static {
fn initialize(scope: Arc<Scope>);
fn update(scope: Arc<Scope>);
fn set_as_default(self) {
let provider: Arc<dyn ScopeProvider> = Arc::new(self);
let _ =
DEFAULT_SCOPE_PROVIDER.swap((Some(Owned::new(provider)), Tag::None), Ordering::SeqCst);
}
}
pub trait ScopeProvider: Send + Sync {
fn provide_scope(&self) -> Result<Arc<Scope>, ServiceNotFoundError>;
}
static DEFAULT_SCOPE_PROVIDER: LazyLock<AtomicOwned<Arc<dyn ScopeProvider>>> =
LazyLock::new(|| {
let provider: Arc<dyn ScopeProvider> = Arc::new(GlobalScopeProvider);
AtomicOwned::new(provider)
});
pub fn get_default_scope() -> Arc<dyn ScopeProvider> {
let guard = Guard::new();
DEFAULT_SCOPE_PROVIDER
.load(Ordering::SeqCst, &guard)
.as_ref()
.map(Arc::clone)
.expect("Default scope provider not initialized")
}
pub fn get_default_scope_full() -> Arc<dyn ScopeProvider> { get_default_scope() }
pub fn set_default_scope<T: ScopeDefault + 'static>(scope: T) {
let provider: Arc<dyn ScopeProvider> = Arc::new(scope);
let _ = DEFAULT_SCOPE_PROVIDER.swap((Some(Owned::new(provider)), Tag::None), Ordering::SeqCst);
}
pub struct GlobalScopeProvider;
impl GlobalScopeProvider {
pub fn refresh_scope() -> Result<Arc<Scope>, ServiceNotFoundError> {
let provider = crate::spo().ok_or(ServiceNotFoundError)?;
Self::refresh_scope_with(&provider)
}
pub fn refresh_scope_with(
provider: &ServiceProvider,
) -> Result<Arc<Scope>, ServiceNotFoundError> {
let Some(scope) = GLOBAL_SCOPE.get() else {
return Err(ServiceNotFoundError);
};
refresh_atomic_scope(scope, provider)
}
}
impl ScopeDefault for GlobalScopeProvider {
fn initialize(scope: Arc<Scope>) { let _ = GLOBAL_SCOPE.set(AtomicOwned::new(scope)); }
fn update(new_scope: Arc<Scope>) {
let Some(scope) = GLOBAL_SCOPE.get() else {
return;
};
let _ = scope.swap((Some(Owned::new(new_scope)), Tag::None), Ordering::SeqCst);
}
}
impl ScopeProvider for GlobalScopeProvider {
fn provide_scope(&self) -> Result<Arc<Scope>, ServiceNotFoundError> {
let Some(scope) = GLOBAL_SCOPE.get() else {
return Err(ServiceNotFoundError);
};
let guard = Guard::new();
scope
.load(Ordering::SeqCst, &guard)
.as_ref()
.map(Arc::clone)
.ok_or(ServiceNotFoundError)
}
}
static GLOBAL_SCOPE: OnceLock<AtomicOwned<Arc<Scope>>> = OnceLock::new();
pub struct ThreadLocalScope;
impl ThreadLocalScope {
pub fn refresh_scope() -> Result<Arc<Scope>, ServiceNotFoundError> {
let provider = crate::spo().ok_or(ServiceNotFoundError)?;
Self::refresh_scope_with(&provider)
}
pub fn refresh_scope_with(
provider: &ServiceProvider,
) -> Result<Arc<Scope>, ServiceNotFoundError> {
TL_SCOPE.with(|cell| {
let Some(scope) = cell.get() else {
return Err(ServiceNotFoundError);
};
refresh_atomic_scope(scope, provider)
})
}
}
impl ScopeDefault for ThreadLocalScope {
fn initialize(scope: Arc<Scope>) { let _ = TL_SCOPE.with(|s| s.set(AtomicOwned::new(scope))); }
fn update(new_scope: Arc<Scope>) {
TL_SCOPE.with(|s| {
let Some(scope) = s.get() else {
return;
};
let _ = scope.swap((Some(Owned::new(new_scope)), Tag::None), Ordering::SeqCst);
});
}
}
impl ScopeProvider for ThreadLocalScope {
fn provide_scope(&self) -> Result<Arc<Scope>, ServiceNotFoundError> {
TL_SCOPE.with(|s| {
let Some(scope) = s.get() else {
return Err(ServiceNotFoundError);
};
let guard = Guard::new();
scope
.load(Ordering::SeqCst, &guard)
.as_ref()
.map(Arc::clone)
.ok_or(ServiceNotFoundError)
})
}
}
thread_local! {
static TL_SCOPE: OnceLock<AtomicOwned<Arc<Scope>>> = OnceLock::new();
}
#[cfg(feature = "tokio")]
pub mod tasks;