diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/run/var/mod.rs (renamed from src/run/var.rs) | 61 | ||||
| -rw-r--r-- | src/run/var/watch.rs | 61 |
2 files changed, 122 insertions, 0 deletions
diff --git a/src/run/var.rs b/src/run/var/mod.rs index 8e22f5c..1ccc195 100644 --- a/src/run/var.rs +++ b/src/run/var/mod.rs @@ -7,11 +7,17 @@ use std::time::Instant; use crate::BString; use crate::bstr; +mod watch; +pub use watch::WatchId; +use watch::{WatchState, WeakWatchId}; + pub struct Vars { simple: HashMap<BString, BString>, magic: HashMap<BString, fn() -> BString>, all: HashSet<BString>, export: HashSet<BString>, + watches: HashMap<WeakWatchId, WatchState>, + watched: HashMap<BString, Vec<WeakWatchId>>, } impl Vars { @@ -38,10 +44,65 @@ impl Vars { magic, all, export, + watches: HashMap::new(), + watched: HashMap::new(), + } + } + + pub fn watch(&mut self, vars: Vec<BString>) -> WatchId { + let watch = WatchId::new(); + let special = vars.iter().any(|v| self.magic.contains_key(v)); + self.watches.insert( + watch.weak(), + WatchState { + special, + dirty: true, + }, + ); + for var in vars { + self.watched + .entry(var) + .or_insert_with(Vec::new) + .push(watch.weak()); + } + watch + } + + pub fn peek_dirty(&self, watch_set: &WatchId) -> bool { + if let Some(w) = self.watches.get(&watch_set.weak()) { + w.dirty || w.special + } else { + false + } + } + + pub fn pop_dirty(&mut self, watch_set: &WatchId) -> bool { + if let Some(w) = self.watches.get_mut(&watch_set.weak()) { + let dirty = w.dirty || w.special; + w.dirty = false; + dirty + } else { + false } } pub fn set(&mut self, var: BString, val: BString) { + if let Some(watchers) = self.watched.get(&var) { + let mut dead = false; + for watcher in watchers { + if watcher.is_dead() { + dead = true; + self.watches.remove(watcher); + } else { + self.watches.get_mut(watcher).unwrap().dirty = true; + } + } + + if dead { + self.watched.get_mut(&var).unwrap().retain(|w| !w.is_dead()); + } + } + self.simple.insert(var.clone(), val); self.all.insert(var); } diff --git a/src/run/var/watch.rs b/src/run/var/watch.rs new file mode 100644 index 0000000..22ac638 --- /dev/null +++ b/src/run/var/watch.rs @@ -0,0 +1,61 @@ +use std::{ + hash::Hash, + sync::{Arc, Weak, atomic::AtomicU32}, +}; + +#[derive(Clone)] +pub struct WatchId { + id: u32, + count: Arc<()>, +} + +#[derive(Clone)] +pub struct WeakWatchId { + id: u32, + count: Weak<()>, +} + +impl PartialEq for WeakWatchId { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for WeakWatchId {} + +impl Hash for WeakWatchId { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.id.hash(state); + } +} + +impl WatchId { + pub(super) fn new() -> Self { + static GEN: AtomicU32 = AtomicU32::new(0); + Self { + id: GEN.fetch_add(1, std::sync::atomic::Ordering::SeqCst), + count: Arc::new(()), + } + } + + pub(super) fn weak(&self) -> WeakWatchId { + WeakWatchId { + id: self.id, + count: Arc::downgrade(&self.count), + } + } +} + +impl WeakWatchId { + pub fn is_dead(&self) -> bool { + self.count.upgrade().is_none() + } +} + +pub struct WatchState { + /// if set, essentially always dirty + pub special: bool, + + /// has the set of watched variables changed? + pub dirty: bool, +} |
