cljrs_value/collections/
transient_set.rs1use super::Snapshot;
2use crate::hash::hash_combine_unordered;
3use crate::{ClojureHash, PersistentHashSet, Value, ValueError, ValueResult};
4use std::sync::{
5 Mutex,
6 atomic::{AtomicBool, AtomicU64, Ordering},
7};
8
9#[derive(Debug)]
10pub struct TransientSet {
11 set: Mutex<PersistentHashSet>,
12 persisted: Mutex<bool>,
13 revision: AtomicU64,
14 keys_may_realize: AtomicBool,
15 #[cfg(feature = "no-gc")]
16 initial: std::cell::RefCell<crate::publish::ValueLease>,
17}
18
19impl TransientSet {
20 pub fn new() -> Self {
21 TransientSet {
22 set: Mutex::new(PersistentHashSet::empty()),
23 persisted: Mutex::new(false),
24 revision: AtomicU64::new(0),
25 #[cfg(feature = "no-gc")]
26 initial: Default::default(),
27 keys_may_realize: AtomicBool::new(false),
28 }
29 }
30
31 pub fn new_from_set(set: &PersistentHashSet) -> TransientSet {
32 TransientSet {
33 set: Mutex::new(set.clone()),
34 persisted: Mutex::new(false),
35 revision: AtomicU64::new(0),
36 #[cfg(feature = "no-gc")]
37 initial: std::cell::RefCell::new(crate::publish::ValueLease::for_trace(
38 set,
39 &Default::default(),
40 )),
41 keys_may_realize: AtomicBool::new(!set.keys_inert()),
42 }
43 }
44
45 fn update<T>(
46 &self,
47 key: &Value,
48 fast: impl FnOnce(&mut PersistentHashSet) -> T,
49 operation: impl Fn(&PersistentHashSet) -> (PersistentHashSet, T),
50 ) -> ValueResult<T> {
51 {
52 let mut collection = self.set.lock().unwrap();
53 if *self.persisted.lock().unwrap() {
54 return Err(ValueError::TransientAlreadyPersisted);
55 }
56 if !self.keys_may_realize.load(Ordering::Relaxed) && crate::realization::is_inert(key) {
57 let result = fast(&mut collection);
58 self.revision.fetch_add(1, Ordering::Relaxed);
59 return Ok(result);
60 }
61 }
62 loop {
63 let (snapshot, revision) = {
64 let set = self.set.lock().unwrap();
65 if *self.persisted.lock().unwrap() {
66 return Err(ValueError::TransientAlreadyPersisted);
67 }
68 (
69 Snapshot::new(set.clone()),
70 self.revision.load(Ordering::Relaxed),
71 )
72 };
73 let (candidate, result) = operation(snapshot.get());
74 let mut set = self.set.lock().unwrap();
75 if *self.persisted.lock().unwrap() {
76 return Err(ValueError::TransientAlreadyPersisted);
77 }
78 if self.revision.load(Ordering::Relaxed) != revision {
79 continue;
80 }
81 *set = candidate;
82 if !crate::realization::is_inert(key) {
83 self.keys_may_realize.store(true, Ordering::Relaxed);
84 }
85 self.revision.fetch_add(1, Ordering::Relaxed);
86 return Ok(result);
87 }
88 }
89 pub fn conj(&self, value: Value) -> ValueResult<()> {
90 #[cfg(feature = "no-gc")]
91 let (value, _value_owner) = crate::publish::prepare_for_owner(value, &Default::default());
92 let entry = self.update(
93 &value,
94 |set| {
95 set.conj_mut(value.clone());
96 set.storage_entry(&value).unwrap()
97 },
98 |set| {
99 let candidate = set.conj(value.clone());
100 let entry = candidate.storage_entry(&value).unwrap();
101 (candidate, entry)
102 },
103 )?;
104 #[cfg(feature = "no-gc")]
105 if !cljrs_gc::alloc_ctx::invocation_is_active() {
106 let lease = cljrs_gc::region::RegionLease::for_value(&entry.1);
107 cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize).set_slot(
108 self as *const _ as usize,
109 entry.0,
110 &lease,
111 );
112 }
113 #[cfg(not(feature = "no-gc"))]
114 let _ = entry;
115 Ok(())
116 }
117 pub fn disj(&self, value: &Value) -> ValueResult<()> {
118 let entry = self.update(
119 value,
120 |set| {
121 let entry = set.storage_entry(value);
122 set.disj_mut(value);
123 entry
124 },
125 |set| (set.disj(value), set.storage_entry(value)),
126 )?;
127 #[cfg(feature = "no-gc")]
128 if let Some((slot, _)) = entry {
129 crate::publish::publish_for_slot(Value::Nil, self as *const _ as usize, slot);
130 }
131 #[cfg(not(feature = "no-gc"))]
132 let _ = entry;
133 Ok(())
134 }
135
136 pub fn persistent(&self) -> ValueResult<PersistentHashSet> {
137 let set = self.set.lock().unwrap();
138 let mut persisted = self.persisted.lock().unwrap();
139 if *persisted {
140 return Err(ValueError::TransientAlreadyPersisted);
141 }
142 *persisted = true;
143 let result = set.clone();
144 #[cfg(feature = "no-gc")]
145 {
146 let address = self as *const _ as usize;
147 if cljrs_gc::region::AllocationOwner::for_address(address).is_permanent() {
148 cljrs_gc::region::RegionLease::for_value(&result).protect_read();
150 } else {
151 cljrs_gc::region::RegionLease::for_address(address).protect_read();
152 }
153 }
154 Ok(result)
155 }
156
157 pub fn count(&self) -> usize {
158 let set = self.set.lock().unwrap();
159 set.count()
160 }
161
162 pub fn contains(&self, value: &Value) -> bool {
163 let set = self.set.lock().unwrap();
164 if !self.keys_may_realize.load(Ordering::Relaxed) && crate::realization::is_inert(value) {
165 return set.contains(value);
166 }
167 let snapshot = Snapshot::new(set.clone());
168 drop(set);
169 snapshot.get().contains(value)
170 }
171}
172
173impl Clone for TransientSet {
174 fn clone(&self) -> Self {
175 Self {
176 set: Mutex::new(self.set.lock().unwrap().clone()),
177 persisted: Mutex::new(*self.persisted.lock().unwrap()),
178 revision: AtomicU64::new(0),
179 #[cfg(feature = "no-gc")]
180 initial: std::cell::RefCell::new(crate::publish::ValueLease::for_trace(
181 &self.set.lock().unwrap().clone(),
182 &Default::default(),
183 )),
184 keys_may_realize: AtomicBool::new(self.keys_may_realize.load(Ordering::Relaxed)),
185 }
186 }
187}
188
189impl ClojureHash for TransientSet {
190 fn clojure_hash(&self) -> u32 {
191 let mut hash: u32 = 0;
192 let snapshot = Snapshot::new(self.set.lock().unwrap().clone());
193 for v in snapshot.get().iter() {
194 hash = hash_combine_unordered(hash, v.clojure_hash())
195 }
196 hash
197 }
198}
199
200impl cljrs_gc::Trace for TransientSet {
201 #[cfg(feature = "no-gc")]
202 fn init_ownership(&self) {
203 let address = self as *const _ as usize;
204 let owner = cljrs_gc::region::AllocationOwner::for_address(address);
205 let lease = self.initial.borrow().reparent(&owner);
206 *self.initial.borrow_mut() = lease;
207 }
208 #[cfg(feature = "no-gc")]
209 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
210 #[cfg(not(feature = "no-gc"))]
211 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
212 {
213 let set = self.set.lock().unwrap();
214 for v in set.iter() {
215 v.trace(visitor);
216 }
217 }
218 }
219
220 fn gc_size_extra(&self) -> usize {
221 let set = self.set.lock().unwrap();
222 set.full_size_extra()
223 }
224}
225
226impl Default for TransientSet {
227 fn default() -> Self {
228 Self::new()
229 }
230}
231
232impl Drop for TransientSet {
233 fn drop(&mut self) {
234 crate::publish::clear_slots(self as *const _ as usize);
235 }
236}