use std::sync::{Arc, atomic::Ordering};
use sdd::{AtomicOwned, Guard};
use tokio::task::futures::TaskLocalFuture;
use crate::{
ServiceProvider,
error::ServiceNotFoundError,
scope::{Scope, ScopeProvider, refresh_atomic_scope},
};
pub type TaskScopeHandle = Arc<AtomicOwned<Arc<Scope>>>;
tokio::task_local! {
static TASK_SCOPE: TaskScopeHandle;
}
pub struct TaskScopeProvider;
impl TaskScopeProvider {
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 handle = TASK_SCOPE.try_get().map_err(|_| ServiceNotFoundError)?;
refresh_atomic_scope(handle.as_ref(), provider)
}
pub fn initialize_async<F>(scope: Scope, fut: F) -> TaskLocalFuture<TaskScopeHandle, F>
where
F: Future,
{
TASK_SCOPE.scope(Arc::new(AtomicOwned::new(Arc::new(scope))), fut)
}
pub fn initialize_sync<F, R>(scope: Scope, fun: F) -> R
where
F: FnOnce() -> R,
{
TASK_SCOPE.sync_scope(Arc::new(AtomicOwned::new(Arc::new(scope))), fun)
}
pub fn update_async<F>(scope: Scope, fut: F) -> TaskLocalFuture<TaskScopeHandle, F>
where
F: Future,
{
TASK_SCOPE.scope(Arc::new(AtomicOwned::new(Arc::new(scope))), fut)
}
pub fn update_sync<F, R>(scope: Scope, fun: F) -> R
where
F: FnOnce() -> R,
{
TASK_SCOPE.sync_scope(Arc::new(AtomicOwned::new(Arc::new(scope))), fun)
}
}
impl super::ScopeDefault for TaskScopeProvider {
fn initialize(_scope: Arc<Scope>) { unreachable!() }
fn update(_scope: Arc<Scope>) { unreachable!() }
}
impl ScopeProvider for TaskScopeProvider {
fn provide_scope(&self) -> Result<Arc<Scope>, ServiceNotFoundError> {
let handle = TASK_SCOPE.try_get().map_err(|_| ServiceNotFoundError)?;
let guard = Guard::new();
handle
.load(Ordering::SeqCst, &guard)
.as_ref()
.map(Arc::clone)
.ok_or(ServiceNotFoundError)
}
}