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
3327560
    pub fn new(container: C) -> Protected<C> {
38
3327560
        let shared = Arc::new(GcMutex::new(container));
39

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

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

            
49
    /// Provides mutable access to the underlying container.
50
38550602
    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
38550602
        ProtectedWriteGuard::new(self.container.write())
53
38550602
    }
54

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

            
61
impl<C: Default + Markable + Send + Transmutable + 'static> Default for Protected<C> {
62
3061246
    fn default() -> Self {
63
3061246
        Protected::new(Default::default())
64
3061246
    }
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
3327560
    fn drop(&mut self) {
109
3327560
        THREAD_TERM_POOL.with_borrow(|tp| {
110
3327560
            tp.drop_container(self.root);
111
3327560
        });
112
3327560
    }
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
38550602
    fn new(reference: GcMutexGuard<'a, C>) -> Self {
129
        #[cfg(debug_assertions)]
130
38550602
        return ProtectedWriteGuard {
131
38550602
            reference,
132
38550602
            protected: RefCell::new(vec![]),
133
38550602
            protected_symbols: RefCell::new(vec![]),
134
38550602
        };
135

            
136
        #[cfg(not(debug_assertions))]
137
        return ProtectedWriteGuard { reference };
138
38550602
    }
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
26580501
    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
26580501
            self.protected
152
26580501
                .borrow_mut()
153
26580501
                .push(transmute::<ATermRef<'_>, ATermRef<'static>>(term.copy()));
154

            
155
26580501
            transmute::<ATermRef<'_>, ATermRef<'static>>(term.copy())
156
        }
157
26580501
    }
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
2420
    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
2420
            self.protected_symbols
168
2420
                .borrow_mut()
169
2420
                .push(transmute::<SymbolRef<'_>, SymbolRef<'static>>(symbol.copy()));
170

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

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

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

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

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

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

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

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

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

            
224
239018311
    fn deref(&self) -> &Self::Target {
225
239018311
        self.reference.transmute_lifetime()
226
239018311
    }
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
}