use std::cell::RefCell;
use std::collections::{HashMap, HashSet, VecDeque};
use std::rc::Rc;
pub type SignalId = usize;
pub type ObserverId = usize;
thread_local! {
static CURRENT_OBSERVER: RefCell<Option<ObserverId>> = const { RefCell::new(None) };
static GRAPH: RefCell<DepGraph> = RefCell::new(DepGraph::default());
}
#[derive(Default)]
struct DepGraph {
next_observer: ObserverId,
edges: HashMap<SignalId, HashSet<ObserverId>>,
back: HashMap<ObserverId, HashSet<SignalId>>,
observers: HashMap<ObserverId, Rc<dyn Fn()>>,
running: HashSet<ObserverId>,
}
impl DepGraph {
fn remove_all_edges_for(&mut self, obs: ObserverId) {
if let Some(signals) = self.back.remove(&obs) {
for s in signals {
if let Some(set) = self.edges.get_mut(&s) {
set.remove(&obs);
}
}
}
}
fn remove_observer(&mut self, obs: ObserverId) {
self.observers.remove(&obs);
self.remove_all_edges_for(obs);
for (_sig, set) in self.edges.iter_mut() {
set.remove(&obs);
}
self.running.remove(&obs);
}
}
pub fn register_signal_read(sig: SignalId) {
CURRENT_OBSERVER.with(|co| {
if let Some(obs) = *co.borrow() {
GRAPH.with(|g| {
let mut g = g.borrow_mut();
g.edges.entry(sig).or_default().insert(obs);
g.back.entry(obs).or_default().insert(sig);
});
}
});
}
pub fn signal_changed(sig: SignalId) {
GRAPH.with(|gcell| {
let mut g = gcell.borrow_mut();
let mut queue: VecDeque<ObserverId> = g
.edges
.get(&sig)
.cloned()
.unwrap_or_default()
.into_iter()
.collect();
while let Some(obs) = queue.pop_front() {
if g.running.contains(&obs) {
continue;
}
g.running.insert(obs);
g.remove_all_edges_for(obs);
let f = g.observers.get(&obs).cloned();
drop(g);
if let Some(f) = f {
CURRENT_OBSERVER.with(|co| {
let prev = *co.borrow();
*co.borrow_mut() = Some(obs);
f();
*co.borrow_mut() = prev;
});
}
g = gcell.borrow_mut();
g.running.remove(&obs);
}
});
}
pub fn new_observer(f: impl Fn() + 'static) -> ObserverId {
GRAPH.with(|g| {
let mut g = g.borrow_mut();
let id = g.next_observer;
g.next_observer += 1;
g.observers.insert(id, Rc::new(f));
id
})
}
pub fn remove_observer(id: ObserverId) {
GRAPH.with(|g| {
let mut g = g.borrow_mut();
g.remove_observer(id);
});
}
pub fn run_observer_now(id: ObserverId) {
let f = GRAPH.with(|gcell| {
let mut g = gcell.borrow_mut();
if !g.running.insert(id) {
return None;
}
g.remove_all_edges_for(id);
let f = g.observers.get(&id).cloned();
drop(g);
if let Some(f) = f {
CURRENT_OBSERVER.with(|co| {
let prev = *co.borrow();
*co.borrow_mut() = Some(id);
f();
*co.borrow_mut() = prev;
});
}
GRAPH.with(|gcell| {
let mut g = gcell.borrow_mut();
g.running.remove(&id);
});
Some(())
});
let _ = f;
}