aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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.rs61
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,
+}