use crate::key::{Dep, DynKey};
use crate::revision::Revision;
use parking_lot::Mutex;
use std::cell::RefCell;
use std::future::Future;
use std::sync::Arc;
use tracing::trace;
tokio::task_local! {
static ACTIVE_STACK: RefCell<Vec<ActiveFrameHandle>>;
}
#[derive(Clone)]
pub struct ActiveFrameHandle(Arc<ActiveFrameInner>);
struct ActiveFrameInner {
dyn_key: DynKey,
started_at: Revision,
deps: Mutex<Vec<Dep>>,
}
impl ActiveFrameHandle {
pub fn new(dyn_key: DynKey, started_at: Revision) -> Self {
Self(Arc::new(ActiveFrameInner {
dyn_key,
started_at,
deps: Mutex::new(Vec::new()),
}))
}
pub fn dyn_key(&self) -> &DynKey {
&self.0.dyn_key
}
pub fn started_at(&self) -> Revision {
self.0.started_at
}
pub fn take_deps(&self) -> Vec<Dep> {
let mut deps = self.0.deps.lock();
std::mem::take(&mut *deps)
}
}
pub struct FrameGuard {
popped: bool,
}
impl Drop for FrameGuard {
fn drop(&mut self) {
if self.popped {
return;
}
let _ = ACTIVE_STACK.try_with(|stack| {
let popped = stack.borrow_mut().pop();
trace!(popped = popped.is_some(), "pop_frame");
});
self.popped = true;
}
}
pub async fn scope_if_needed<F, Fut, R>(f: F) -> R
where
F: FnOnce() -> Fut,
Fut: Future<Output = R>,
{
if ACTIVE_STACK.try_with(|_| ()).is_ok() {
f().await
} else {
ACTIVE_STACK.scope(RefCell::new(Vec::new()), f()).await
}
}
pub async fn scope_if_needed_boxed<R>(
fut: std::pin::Pin<Box<dyn Future<Output = R> + Send + '_>>,
) -> R {
if ACTIVE_STACK.try_with(|_| ()).is_ok() {
fut.await
} else {
ACTIVE_STACK.scope(RefCell::new(Vec::new()), fut).await
}
}
pub fn has_active_frame() -> bool {
ACTIVE_STACK
.try_with(|stack| !stack.borrow().is_empty())
.unwrap_or(false)
}
pub fn record_dep(dep: Dep) {
let _ = ACTIVE_STACK.try_with(|stack| {
if let Some(top) = stack.borrow().last() {
top.0.deps.lock().push(dep);
}
});
}
pub fn find_cycle(requested: &DynKey) -> Option<Vec<DynKey>> {
ACTIVE_STACK
.try_with(|stack| {
let stack = stack.borrow();
let has_cycle = stack.iter().any(|f| f.dyn_key() == requested);
if !has_cycle {
return None;
}
Some(stack.iter().map(|f| f.dyn_key().clone()).collect())
})
.ok()
.flatten()
}
pub fn push_frame(frame: ActiveFrameHandle) -> FrameGuard {
let _ = ACTIVE_STACK.try_with(|stack| {
stack.borrow_mut().push(frame);
});
FrameGuard { popped: false }
}