1
#[cfg(debug_assertions)]
2
use std::cell::RefCell;
3
use std::fmt::Debug;
4
use std::hash::Hash;
5
use std::mem::transmute;
6
use std::ops::Deref;
7
use std::ops::DerefMut;
8
use std::sync::Arc;
9

            
10
use merc_collections::ProtectionIndex;
11
use merc_utilities::PhantomUnsend;
12

            
13
use crate::Markable;
14
use crate::Symb;
15
use crate::SymbolRef;
16
use crate::Term;
17
use crate::Transmutable;
18
use crate::aterm::ATermRef;
19
use crate::storage::GcMutex;
20
use crate::storage::GcMutexGuard;
21
use crate::storage::THREAD_TERM_POOL;
22

            
23
/// A container of objects, typically either terms or objects containing terms,
24
/// that are of trait Markable. These store ATermRef<'static> that are protected
25
/// during garbage collection by being in the container itself.
26
pub struct Protected<C> {
27
    container: Arc<GcMutex<C>>,
28
    root: ProtectionIndex,
29

            
30
    // Protected is not Send because it uses thread-local state for its protection
31
    // mechanism.
32
    _unsend: PhantomUnsend,
33
}
34

            
35
impl<C: Markable + Send + Transmutable + 'static> Protected<C> {
36
    /// Creates a new Protected container from a given container.
37
3335748
    pub fn new(container: C) -> Protected<C> {
38
3335748
        let shared = Arc::new(GcMutex::new(container));
39

            
40
3335748
        let root = THREAD_TERM_POOL.with_borrow(|tp| tp.protect_container(shared.clone()));
41

            
42
3335748
        Protected {
43
3335748
            container: shared,
44
3335748
            root,
45
3335748
            _unsend: Default::default(),
46
3335748
        }
47
3335748
    }
48

            
49
    /// Provides mutable access to the underlying container.
50
38595387
    pub fn write(&mut self) -> ProtectedWriteGuard<'_, C> {
51
        // The lifetime of ATermRef can be derived from self since it is protected by self, so transmute 'static into 'a.
52
38595387
        ProtectedWriteGuard::new(self.container.write())
53
38595387
    }
54

            
55
    /// Provides immutable access to the underlying container.
56
25705648
    pub fn read(&self) -> ProtectedReadGuard<'_, C> {
57
25705648
        ProtectedReadGuard::new(self.container.read())
58
25705648
    }
59
}
60

            
61
impl<C: Default + Markable + Send + Transmutable + 'static> Default for Protected<C> {
62
3067560
    fn default() -> Self {
63
3067560
        Protected::new(Default::default())
64
3067560
    }
65
}
66

            
67
impl<C: Clone + Markable + Send + Transmutable + 'static> Clone for Protected<C> {
68
    fn clone(&self) -> Self {
69
        Protected::new(self.container.read().clone())
70
    }
71
}
72

            
73
impl<C: Hash + Markable> Hash for Protected<C> {
74
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
75
        self.container.read().hash(state)
76
    }
77
}
78

            
79
impl<C: PartialEq + Markable> PartialEq for Protected<C> {
80
1
    fn eq(&self, other: &Self) -> bool {
81
1
        self.container.read().eq(&other.container.read())
82
1
    }
83
}
84

            
85
impl<C: PartialOrd + Markable> PartialOrd for Protected<C> {
86
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
87
        let c: &C = &other.container.read();
88
        self.container.read().partial_cmp(c)
89
    }
90
}
91

            
92
impl<C: Debug + Markable> Debug for Protected<C> {
93
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94
        let c: &C = &self.container.read();
95
        write!(f, "{c:?}")
96
    }
97
}
98

            
99
impl<C: Eq + PartialEq + Markable> Eq for Protected<C> {}
100
impl<C: Ord + PartialEq + PartialOrd + Markable> Ord for Protected<C> {
101
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
102
        let c: &C = &other.container.read();
103
        self.container.read().partial_cmp(c).unwrap()
104
    }
105
}
106

            
107
impl<C> Drop for Protected<C> {
108
3335748
    fn drop(&mut self) {
109
3335748
        THREAD_TERM_POOL.with_borrow(|tp| {
110
3335748
            tp.drop_container(self.root);
111
3335748
        });
112
3335748
    }
113
}
114

            
115
pub struct ProtectedWriteGuard<'a, C: Markable> {
116
    reference: GcMutexGuard<'a, C>,
117

            
118
    /// Terms that have been protected during the lifetime of this guard.
119
    #[cfg(debug_assertions)]
120
    protected: RefCell<Vec<ATermRef<'static>>>,
121

            
122
    /// Symbols that have been protected during the lifetime of this guard.
123
    #[cfg(debug_assertions)]
124
    protected_symbols: RefCell<Vec<SymbolRef<'static>>>,
125
}
126

            
127
impl<'a, C: Markable> ProtectedWriteGuard<'a, C> {
128
38595387
    fn new(reference: GcMutexGuard<'a, C>) -> Self {
129
        #[cfg(debug_assertions)]
130
38595387
        return ProtectedWriteGuard {
131
38595387
            reference,
132
38595387
            protected: RefCell::new(vec![]),
133
38595387
            protected_symbols: RefCell::new(vec![]),
134
38595387
        };
135

            
136
        #[cfg(not(debug_assertions))]
137
        return ProtectedWriteGuard { reference };
138
38595387
    }
139

            
140
    /// Yields a term to insert into the container.
141
    ///
142
    /// # Safety
143
    ///
144
    /// The invariant to uphold is that the resulting term MUST be inserted into the container. This is checked in debug mode, but not in release mode. If this invariant is violated, undefined behaviour may occur during garbage collection.
145
    /// We do not mark this function unsafe since that would make its use cumbersome.
146
26604413
    pub fn protect<'b>(&self, term: &'b impl Term<'a, 'b>) -> ATermRef<'static> {
147
        unsafe {
148
            // Store terms that are marked as protected to check if they are
149
            // actually in the container when the protection is dropped.
150
            #[cfg(debug_assertions)]
151
26604413
            self.protected
152
26604413
                .borrow_mut()
153
26604413
                .push(transmute::<ATermRef<'_>, ATermRef<'static>>(term.copy()));
154

            
155
26604413
            transmute::<ATermRef<'_>, ATermRef<'static>>(term.copy())
156
        }
157
26604413
    }
158

            
159
    /// Yields a symbol to insert into the container.
160
    ///
161
    /// The invariant to uphold is that the resulting symbol MUST be inserted into the container.
162
3250
    pub fn protect_symbol<'b>(&self, symbol: &'b impl Symb<'a, 'b>) -> SymbolRef<'static> {
163
        unsafe {
164
            // Store symbols that are marked as protected to check if they are
165
            // actually in the container when the protection is dropped.
166
            #[cfg(debug_assertions)]
167
3250
            self.protected_symbols
168
3250
                .borrow_mut()
169
3250
                .push(transmute::<SymbolRef<'_>, SymbolRef<'static>>(symbol.copy()));
170

            
171
3250
            transmute::<SymbolRef<'_>, SymbolRef<'static>>(symbol.copy())
172
        }
173
3250
    }
174
}
175

            
176
#[cfg(debug_assertions)]
177
impl<C: Markable> Drop for ProtectedWriteGuard<'_, C> {
178
38595387
    fn drop(&mut self) {
179
        {
180
38595387
            for term in self.protected.borrow().iter() {
181
26604413
                debug_assert!(
182
26604413
                    self.reference.contains_term(term),
183
                    "Term was protected but not actually inserted"
184
                );
185
            }
186

            
187
38595387
            for symbol in self.protected_symbols.borrow().iter() {
188
3250
                debug_assert!(
189
3250
                    self.reference.contains_symbol(symbol),
190
                    "Symbol was protected but not actually inserted"
191
                );
192
            }
193
        }
194
38595387
    }
195
}
196

            
197
impl<'a, C: Markable + Transmutable + 'a> Deref for ProtectedWriteGuard<'a, C> {
198
    type Target = C::Target<'a>;
199

            
200
23566193
    fn deref(&self) -> &Self::Target {
201
23566193
        self.reference.transmute_lifetime()
202
23566193
    }
203
}
204

            
205
impl<C: Markable + Transmutable> DerefMut for ProtectedWriteGuard<'_, C> {
206
60552052
    fn deref_mut(&mut self) -> &mut Self::Target {
207
60552052
        self.reference.deref_mut().transmute_lifetime_mut()
208
60552052
    }
209
}
210

            
211
pub struct ProtectedReadGuard<'a, C> {
212
    reference: GcMutexGuard<'a, C>,
213
}
214

            
215
impl<'a, C> ProtectedReadGuard<'a, C> {
216
25705648
    fn new(reference: GcMutexGuard<'a, C>) -> Self {
217
25705648
        Self { reference }
218
25705648
    }
219
}
220

            
221
impl<'a, C: Transmutable> Deref for ProtectedReadGuard<'a, C> {
222
    type Target = C::Target<'a>;
223

            
224
236794801
    fn deref(&self) -> &Self::Target {
225
236794801
        self.reference.transmute_lifetime()
226
236794801
    }
227
}
228

            
229
#[cfg(test)]
230
mod tests {
231
    use crate::ATerm;
232

            
233
    use super::*;
234

            
235
    #[test]
236
1
    fn test_aterm_container() {
237
1
        let _ = merc_utilities::test_logger();
238

            
239
1
        let t = ATerm::from_string("f(g(a),b)").unwrap();
240

            
241
        // First test the trait for a standard container.
242
1
        let mut container = Protected::<Vec<ATermRef<'static>>>::new(vec![]);
243

            
244
1000
        for _ in 0..1000 {
245
1000
            let mut write = container.write();
246
1000
            write.push(t.get());
247
1000
        }
248
1
    }
249
}