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

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

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

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

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

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

            
136
        #[cfg(not(debug_assertions))]
137
        return ProtectedWriteGuard { reference };
138
38544659
    }
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
26577777
    pub fn protect<'b, T: Term<'a, 'b>>(&self, term: &'b T) -> 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
26577777
            self.protected
152
26577777
                .borrow_mut()
153
26577777
                .push(transmute::<ATermRef<'_>, ATermRef<'static>>(term.copy()));
154

            
155
26577777
            transmute::<ATermRef<'_>, ATermRef<'static>>(term.copy())
156
        }
157
26577777
    }
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, S: Symb<'a, 'b>>(&self, symbol: &'b S) -> 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
38544659
    fn drop(&mut self) {
179
        {
180
38544659
            for term in self.protected.borrow().iter() {
181
26577777
                debug_assert!(
182
26577777
                    self.reference.contains_term(term),
183
                    "Term was protected but not actually inserted"
184
                );
185
            }
186

            
187
38544659
            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
38544659
    }
195
}
196

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

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

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

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

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

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

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