1use 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 #[cfg(feature = "no-gc")]
24 initial: std::cell::RefCell<crate::publish::ValueLease>,
25}
26
27impl TransientMap {
28 pub fn new() -> TransientMap {
29 TransientMap {
30 map: Mutex::new(PersistentHashMap::empty()),
31 persisted: Mutex::new(false),
32 revision: AtomicU64::new(0),
33 #[cfg(feature = "no-gc")]
34 initial: Default::default(),
35 keys_may_realize: AtomicBool::new(false),
36 }
37 }
38
39 pub fn new_from_map(map: &PersistentHashMap) -> TransientMap {
40 TransientMap {
41 map: Mutex::new(map.clone()),
42 persisted: Mutex::new(false),
43 revision: AtomicU64::new(0),
44 #[cfg(feature = "no-gc")]
45 initial: std::cell::RefCell::new(crate::publish::ValueLease::for_trace(
46 map,
47 &Default::default(),
48 )),
49 keys_may_realize: AtomicBool::new(!map.keys_inert()),
50 }
51 }
52
53 fn update<T>(
54 &self,
55 key: &Value,
56 fast: impl FnOnce(&mut PersistentHashMap) -> T,
57 operation: impl Fn(&PersistentHashMap) -> (PersistentHashMap, T),
58 ) -> ValueResult<T> {
59 {
60 let mut collection = self.map.lock().unwrap();
61 if *self.persisted.lock().unwrap() {
62 return Err(ValueError::TransientAlreadyPersisted);
63 }
64 if !self.keys_may_realize.load(Ordering::Relaxed) && crate::realization::is_inert(key) {
65 let result = fast(&mut collection);
66 self.revision.fetch_add(1, Ordering::Relaxed);
67 return Ok(result);
68 }
69 }
70 loop {
71 let (snapshot, revision) = {
72 let map = self.map.lock().unwrap();
73 if *self.persisted.lock().unwrap() {
74 return Err(ValueError::TransientAlreadyPersisted);
75 }
76 (
77 Snapshot::new(map.clone()),
78 self.revision.load(Ordering::Relaxed),
79 )
80 };
81 let (candidate, result) = operation(snapshot.get());
82 let mut map = self.map.lock().unwrap();
83 if *self.persisted.lock().unwrap() {
84 return Err(ValueError::TransientAlreadyPersisted);
85 }
86 if self.revision.load(Ordering::Relaxed) != revision {
87 continue;
88 }
89 *map = candidate;
90 if !crate::realization::is_inert(key) {
91 self.keys_may_realize.store(true, Ordering::Relaxed);
92 }
93 self.revision.fetch_add(1, Ordering::Relaxed);
94 return Ok(result);
95 }
96 }
97 pub fn assoc(&self, key: Value, value: Value) -> ValueResult<()> {
98 #[cfg(feature = "no-gc")]
99 let (key, _key_owner) = crate::publish::prepare_for_owner(key, &Default::default());
100 #[cfg(feature = "no-gc")]
101 let (value, _value_owner) = crate::publish::prepare_for_owner(value, &Default::default());
102 let entry = self.update(
103 &key,
104 |map| {
105 map.assoc_mut(key.clone(), value.clone());
106 map.storage_entry(&key).unwrap()
107 },
108 |map| {
109 let candidate = map.assoc(key.clone(), value.clone());
110 let entry = candidate.storage_entry(&key).unwrap();
111 (candidate, entry)
112 },
113 )?;
114 #[cfg(feature = "no-gc")]
115 if !cljrs_gc::alloc_ctx::invocation_is_active() {
116 let lease = cljrs_gc::region::RegionLease::for_values(&[entry.1, value]);
117 cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize).set_slot(
118 self as *const _ as usize,
119 entry.0,
120 &lease,
121 );
122 }
123 #[cfg(not(feature = "no-gc"))]
124 let _ = entry;
125 Ok(())
126 }
127 pub fn dissoc(&self, key: &Value) -> ValueResult<()> {
128 let entry = self.update(
129 key,
130 |map| {
131 let entry = map.storage_entry(key);
132 map.dissoc_mut(key);
133 entry
134 },
135 |map| (map.dissoc(key), map.storage_entry(key)),
136 )?;
137 #[cfg(feature = "no-gc")]
138 if let Some((slot, _)) = entry {
139 crate::publish::publish_for_slot(Value::Nil, self as *const _ as usize, slot);
140 }
141 #[cfg(not(feature = "no-gc"))]
142 let _ = entry;
143 Ok(())
144 }
145
146 pub fn persistent(&self) -> ValueResult<PersistentHashMap> {
147 let map = self.map.lock().unwrap();
148 let mut persisted = self.persisted.lock().unwrap();
149 if *persisted {
150 return Err(ValueError::TransientAlreadyPersisted);
151 }
152 *persisted = true;
153 let result = map.clone();
154 #[cfg(feature = "no-gc")]
155 {
156 let address = self as *const _ as usize;
157 if cljrs_gc::region::AllocationOwner::for_address(address).is_permanent() {
158 cljrs_gc::region::RegionLease::for_value(&result).protect_read();
160 } else {
161 cljrs_gc::region::RegionLease::for_address(address).protect_read();
162 }
163 }
164 Ok(result)
165 }
166
167 fn lookup<T>(&self, key: &Value, operation: impl Fn(&PersistentHashMap) -> T) -> T {
168 let map = self.map.lock().unwrap();
169 if !self.keys_may_realize.load(Ordering::Relaxed) && crate::realization::is_inert(key) {
170 return operation(&map);
171 }
172 let snapshot = Snapshot::new(map.clone());
173 drop(map);
174 operation(snapshot.get())
175 }
176 pub fn find(&self, key: &Value) -> Option<(Value, Value)> {
177 self.lookup(key, |map| {
178 map.get(key).map(|value| {
179 #[cfg(feature = "no-gc")]
180 cljrs_gc::region::RegionLease::for_values(&[key.clone(), value.clone()])
181 .protect_read();
182 (key.clone(), value.clone())
183 })
184 })
185 }
186 pub fn get(&self, key: &Value) -> Option<Value> {
187 self.lookup(key, |map| {
188 let value = map.get(key).cloned();
189 #[cfg(feature = "no-gc")]
190 if let Some(value) = &value {
191 cljrs_gc::region::RegionLease::for_value(value).protect_read();
192 }
193 value
194 })
195 }
196 pub fn contains_key(&self, key: &Value) -> bool {
197 self.lookup(key, |map| map.contains_key(key))
198 }
199
200 pub fn count(&self) -> usize {
201 let map = self.map.lock().unwrap();
202 map.count()
203 }
204}
205
206impl Clone for TransientMap {
207 fn clone(&self) -> Self {
208 Self {
209 map: Mutex::new(self.map.lock().unwrap().clone()),
210 persisted: Mutex::new(*self.persisted.lock().unwrap()),
211 revision: AtomicU64::new(0),
212 #[cfg(feature = "no-gc")]
213 initial: std::cell::RefCell::new(crate::publish::ValueLease::for_trace(
214 &self.map.lock().unwrap().clone(),
215 &Default::default(),
216 )),
217 keys_may_realize: AtomicBool::new(self.keys_may_realize.load(Ordering::Relaxed)),
218 }
219 }
220}
221
222impl ClojureHash for TransientMap {
223 fn clojure_hash(&self) -> u32 {
224 let mut hash: u32 = 0;
225 let snapshot = Snapshot::new(self.map.lock().unwrap().clone());
226 for (k, v) in snapshot.get().iter() {
227 hash = hash_combine_unordered(
228 hash,
229 hash_combine_ordered(k.clojure_hash(), v.clojure_hash()),
230 );
231 }
232 hash
233 }
234}
235
236impl cljrs_gc::Trace for TransientMap {
237 #[cfg(feature = "no-gc")]
238 fn init_ownership(&self) {
239 let address = self as *const _ as usize;
240 let owner = cljrs_gc::region::AllocationOwner::for_address(address);
241 let lease = self.initial.borrow().reparent(&owner);
242 *self.initial.borrow_mut() = lease;
243 }
244 #[cfg(feature = "no-gc")]
245 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
246 #[cfg(not(feature = "no-gc"))]
247 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
248 {
249 let map = self.map.lock().unwrap();
250 for (k, v) in map.iter() {
251 k.trace(visitor);
252 v.trace(visitor);
253 }
254 }
255 }
256
257 fn gc_size_extra(&self) -> usize {
258 let map = self.map.lock().unwrap();
259 map.full_size_extra()
260 }
261}
262
263impl Default for TransientMap {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269impl Drop for TransientMap {
270 fn drop(&mut self) {
271 crate::publish::clear_slots(self as *const _ as usize);
272 }
273}