use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::{Arc, Mutex};
use lazily::ReactiveGraph;
pub type Log = Arc<Mutex<Vec<String>>>;
pub fn log_push(log: &Log, name: &str) {
log.lock().unwrap().push(name.to_owned());
}
pub fn log_snapshot(log: &Log) -> Vec<String> {
log.lock().unwrap().clone()
}
pub type Poison = Arc<AtomicBool>;
#[derive(Debug, Default)]
pub struct ComputeCounter {
count: AtomicUsize,
armed: AtomicUsize,
}
pub type Computes = Arc<ComputeCounter>;
pub const COMPUTE_FAILED: &str = "reactive-graph: compute_failed (fail_next)";
pub fn count_computes(computes: &Computes) -> bool {
use std::sync::atomic::Ordering::SeqCst;
computes.count.fetch_add(1, SeqCst);
loop {
let n = computes.armed.load(SeqCst);
if n == 0 {
return false;
}
if computes
.armed
.compare_exchange(n, n - 1, SeqCst, SeqCst)
.is_ok()
{
return true;
}
}
}
pub fn arm_failure(computes: &Computes, n: usize) {
computes
.armed
.fetch_add(n.max(1), std::sync::atomic::Ordering::SeqCst);
}
pub fn computes_seen(computes: &Computes) -> usize {
computes.count.load(std::sync::atomic::Ordering::SeqCst)
}
pub type Merges = Arc<AtomicUsize>;
pub fn count_merge(merges: &Merges) {
merges.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
pub fn merges_seen(merges: &Merges) -> usize {
merges.load(std::sync::atomic::Ordering::SeqCst)
}
pub enum Ref<G: ReactiveGraph> {
Cell(G::Source<i64>),
Slot(G::Computed<i64>),
Effect(G::Effect),
}
impl<G: ReactiveGraph> Clone for Ref<G> {
fn clone(&self) -> Self {
*self
}
}
impl<G: ReactiveGraph> Copy for Ref<G> {}
pub fn dispose<G: ReactiveGraph>(graph: &G, node: Ref<G>) {
match node {
Ref::Cell(h) => graph.dispose_cell(&h),
Ref::Slot(h) => graph.dispose_slot(&h),
Ref::Effect(h) => graph.dispose_effect(&h),
}
}
pub fn dependents_of<G: ReactiveGraph>(graph: &G, node: Ref<G>) -> usize {
match node {
Ref::Cell(h) => graph.dependent_count(&h),
Ref::Slot(h) => graph.dependent_count(&h),
Ref::Effect(h) => graph.dependent_count(&h),
}
}
pub fn dependencies_of<G: ReactiveGraph>(graph: &G, node: Ref<G>) -> usize {
match node {
Ref::Cell(h) => graph.dependency_count(&h),
Ref::Slot(h) => graph.dependency_count(&h),
Ref::Effect(h) => graph.dependency_count(&h),
}
}
pub trait ScopeModel<M: GraphModel> {
fn source(&self, value: i64) -> <M::Graph as ReactiveGraph>::Source<i64>;
fn computed(
&self,
reads: &[Ref<M::Graph>],
offset: i64,
computes: &Computes,
) -> <M::Graph as ReactiveGraph>::Computed<i64>;
fn effect(&self, name: &str, reads: &[Ref<M::Graph>]) -> <M::Graph as ReactiveGraph>::Effect;
fn owned(&self) -> usize;
fn disarm(self);
}
pub trait GraphModel: Sized {
type Graph: ReactiveGraph;
type Scope<'a>: ScopeModel<Self>
where
Self: 'a;
type Signal;
const NAME: &'static str;
fn create() -> Self;
fn graph(&self) -> &Self::Graph;
fn source(&self, value: i64) -> <Self::Graph as ReactiveGraph>::Source<i64>;
fn computed(
&self,
reads: &[Ref<Self::Graph>],
offset: i64,
computes: &Computes,
) -> <Self::Graph as ReactiveGraph>::Computed<i64>;
fn effect(
&self,
name: &str,
reads: &[Ref<Self::Graph>],
) -> <Self::Graph as ReactiveGraph>::Effect;
fn signal(&self, reads: &[Ref<Self::Graph>], offset: i64, computes: &Computes) -> Self::Signal;
fn read_signal(&self, signal: &Self::Signal) -> Result<i64, ()>;
fn dispose_signal(&self, signal: &Self::Signal);
fn merge_cell(&self, value: i64) -> <Self::Graph as ReactiveGraph>::Source<i64> {
self.source(value)
}
fn merge(&self, cell: <Self::Graph as ReactiveGraph>::Source<i64>, op: i64);
fn feed_effect(
&self,
name: &str,
reads: &[Ref<Self::Graph>],
target: <Self::Graph as ReactiveGraph>::Source<i64>,
merges: &Merges,
) -> <Self::Graph as ReactiveGraph>::Effect;
fn diverge_effect(
&self,
name: &str,
own: <Self::Graph as ReactiveGraph>::Source<i64>,
) -> <Self::Graph as ReactiveGraph>::Effect;
fn drain_exhausted(&self) -> bool {
false
}
fn clear_drain(&self) {}
fn batch(
&self,
writes: &[(<Self::Graph as ReactiveGraph>::Source<i64>, i64)],
merges: &[(<Self::Graph as ReactiveGraph>::Source<i64>, i64)],
);
fn read(&self, node: Ref<Self::Graph>) -> Result<i64, ()>;
fn set_cell(&self, cell: <Self::Graph as ReactiveGraph>::Source<i64>, value: i64);
fn is_effect_active(&self, effect: <Self::Graph as ReactiveGraph>::Effect) -> bool;
fn scope(&self) -> Self::Scope<'_>;
fn run_log(&self) -> &Log;
fn cleanup_log(&self) -> &Log;
fn poison(&self) -> &Poison;
fn settle(&self) {}
}