Skip to main content

cljrs_value/collections/
transient_set.rs

1use 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                // Rust stack containers do not have a containing region to lease.
149                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}