cljrs_value/collections/
transient_map.rs1use super::Snapshot;
10use crate::hash::{hash_combine_ordered, hash_combine_unordered};
11use crate::{ClojureHash, PersistentHashMap, Value, ValueError, ValueResult};
12use std::sync::{
13 Mutex,
14 atomic::{AtomicBool, AtomicU64, Ordering},
15};
16
17#[derive(Debug)]
18pub struct TransientMap {
19 map: Mutex<PersistentHashMap>,
20 persisted: Mutex<bool>,
21 revision: AtomicU64,
22 keys_may_realize: AtomicBool,
23}
24
25impl TransientMap {
26 pub fn new() -> TransientMap {
27 TransientMap {
28 map: Mutex::new(PersistentHashMap::empty()),
29 persisted: Mutex::new(false),
30 revision: AtomicU64::new(0),
31 keys_may_realize: AtomicBool::new(false),
32 }
33 }
34
35 pub fn new_from_map(map: &PersistentHashMap) -> TransientMap {
36 TransientMap {
37 map: Mutex::new(map.clone()),
38 persisted: Mutex::new(false),
39 revision: AtomicU64::new(0),
40 keys_may_realize: AtomicBool::new(
41 !(map.iter().all(|(key, _)| crate::realization::is_inert(key))),
42 ),
43 }
44 }
45
46 fn update(
47 &self,
48 key: &Value,
49 fast: impl FnOnce(&mut PersistentHashMap),
50 operation: impl Fn(&PersistentHashMap) -> PersistentHashMap,
51 ) -> ValueResult<()> {
52 {
53 let mut collection = self.map.lock().unwrap();
54 if *self.persisted.lock().unwrap() {
55 return Err(ValueError::TransientAlreadyPersisted);
56 }
57 if !self.keys_may_realize.load(Ordering::Relaxed) && crate::realization::is_inert(key) {
58 fast(&mut collection);
59 self.revision.fetch_add(1, Ordering::Relaxed);
60 return Ok(());
61 }
62 }
63 loop {
64 let (snapshot, revision) = {
65 let map = self.map.lock().unwrap();
66 if *self.persisted.lock().unwrap() {
67 return Err(ValueError::TransientAlreadyPersisted);
68 }
69 (
70 Snapshot::new(map.clone()),
71 self.revision.load(Ordering::Relaxed),
72 )
73 };
74 let candidate = operation(snapshot.get());
75 let mut map = self.map.lock().unwrap();
76 if *self.persisted.lock().unwrap() {
77 return Err(ValueError::TransientAlreadyPersisted);
78 }
79 if self.revision.load(Ordering::Relaxed) != revision {
80 continue;
81 }
82 *map = candidate;
83 if !crate::realization::is_inert(key) {
84 self.keys_may_realize.store(true, Ordering::Relaxed);
85 }
86 self.revision.fetch_add(1, Ordering::Relaxed);
87 return Ok(());
88 }
89 }
90 pub fn assoc(&self, key: Value, value: Value) -> ValueResult<()> {
91 self.update(
92 &key,
93 |map| map.assoc_mut(key.clone(), value.clone()),
94 |map| map.assoc(key.clone(), value.clone()),
95 )
96 }
97 pub fn dissoc(&self, key: &Value) -> ValueResult<()> {
98 self.update(key, |map| map.dissoc_mut(key), |map| map.dissoc(key))
99 }
100
101 pub fn persistent(&self) -> ValueResult<PersistentHashMap> {
102 let map = self.map.lock().unwrap();
103 let mut persisted = self.persisted.lock().unwrap();
104 if *persisted {
105 return Err(ValueError::TransientAlreadyPersisted);
106 }
107 *persisted = true;
108 Ok(map.clone())
109 }
110
111 fn lookup<T>(&self, key: &Value, operation: impl Fn(&PersistentHashMap) -> T) -> T {
112 let map = self.map.lock().unwrap();
113 if !self.keys_may_realize.load(Ordering::Relaxed) && crate::realization::is_inert(key) {
114 return operation(&map);
115 }
116 let snapshot = Snapshot::new(map.clone());
117 drop(map);
118 operation(snapshot.get())
119 }
120 pub fn find(&self, key: &Value) -> Option<(Value, Value)> {
121 self.lookup(key, |map| {
122 map.get(key).map(|value| (key.clone(), value.clone()))
123 })
124 }
125 pub fn get(&self, key: &Value) -> Option<Value> {
126 self.lookup(key, |map| map.get(key).cloned())
127 }
128 pub fn contains_key(&self, key: &Value) -> bool {
129 self.lookup(key, |map| map.contains_key(key))
130 }
131
132 pub fn count(&self) -> usize {
133 let map = self.map.lock().unwrap();
134 map.count()
135 }
136}
137
138impl Clone for TransientMap {
139 fn clone(&self) -> Self {
140 Self {
141 map: Mutex::new(self.map.lock().unwrap().clone()),
142 persisted: Mutex::new(*self.persisted.lock().unwrap()),
143 revision: AtomicU64::new(0),
144 keys_may_realize: AtomicBool::new(self.keys_may_realize.load(Ordering::Relaxed)),
145 }
146 }
147}
148
149impl ClojureHash for TransientMap {
150 fn clojure_hash(&self) -> u32 {
151 let mut hash: u32 = 0;
152 let snapshot = Snapshot::new(self.map.lock().unwrap().clone());
153 for (k, v) in snapshot.get().iter() {
154 hash = hash_combine_unordered(
155 hash,
156 hash_combine_ordered(k.clojure_hash(), v.clojure_hash()),
157 );
158 }
159 hash
160 }
161}
162
163impl cljrs_gc::Trace for TransientMap {
164 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
165 {
166 let map = self.map.lock().unwrap();
167 for (k, v) in map.iter() {
168 k.trace(visitor);
169 v.trace(visitor);
170 }
171 }
172 }
173
174 fn gc_size_extra(&self) -> usize {
175 let map = self.map.lock().unwrap();
176 map.full_size_extra()
177 }
178}
179
180impl Default for TransientMap {
181 fn default() -> Self {
182 Self::new()
183 }
184}