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_unsafety::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::GcMutexReadGuard;
22
use crate::storage::THREAD_TERM_POOL;
23

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

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

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

            
41
1166058
        let root = THREAD_TERM_POOL.with(|tp| tp.protect_container(shared.clone()));
42

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

            
50
    /// Provides mutable access to the underlying container, returning a [ProtectedWriteGuard].
51
115647291
    pub fn write(&mut self) -> ProtectedWriteGuard<'_, C> {
52
        // SAFETY: Protected is `!Send` so it is only ever used from one thread,
53
        // and `write` takes `&mut self`, so no other guard from this handle
54
        // overlaps. The only other access is the global garbage collector,
55
        // which only accesses the container when this handle is dropped, so it
56
        // cannot overlap either.
57
115647291
        let mutex = unsafe { &mut *(Arc::as_ptr(&self.container) as *mut GcMutex<C>) };
58
115647291
        ProtectedWriteGuard::new(mutex.lock_mut())
59
115647291
    }
60

            
61
    /// Provides immutable access to the underlying container, returning a [ProtectedReadGuard].
62
77749309
    pub fn read(&self) -> ProtectedReadGuard<'_, C> {
63
77749309
        ProtectedReadGuard::new(self.container.lock())
64
77749309
    }
65
}
66

            
67
impl<C: Default + Markable + Send + Sync + Transmutable + 'static> Default for Protected<C> {
68
369574
    fn default() -> Self {
69
369574
        Protected::new(Default::default())
70
369574
    }
71
}
72

            
73
impl<C: Clone + Markable + Send + Sync + Transmutable + 'static> Clone for Protected<C> {
74
    fn clone(&self) -> Self {
75
        Protected::new(self.container.lock().clone())
76
    }
77
}
78

            
79
impl<C: Hash + Markable> Hash for Protected<C> {
80
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
81
        self.container.lock().hash(state)
82
    }
83
}
84

            
85
impl<C: PartialEq + Markable> PartialEq for Protected<C> {
86
1
    fn eq(&self, other: &Self) -> bool {
87
1
        self.container.lock().eq(&other.container.lock())
88
1
    }
89
}
90

            
91
impl<C: PartialOrd + Markable> PartialOrd for Protected<C> {
92
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
93
        let c: &C = &other.container.lock();
94
        self.container.lock().partial_cmp(c)
95
    }
96
}
97

            
98
impl<C: Debug + Markable> Debug for Protected<C> {
99
6
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100
6
        let c: &C = &self.container.lock();
101
6
        write!(f, "{c:?}")
102
6
    }
103
}
104

            
105
impl<C: Eq + PartialEq + Markable> Eq for Protected<C> {}
106
impl<C: Ord + PartialEq + PartialOrd + Markable> Ord for Protected<C> {
107
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
108
        let c: &C = &other.container.lock();
109
        self.container.lock().partial_cmp(c).unwrap()
110
    }
111
}
112

            
113
impl<C> Drop for Protected<C> {
114
1166058
    fn drop(&mut self) {
115
1166058
        THREAD_TERM_POOL.with(|tp| {
116
1166058
            tp.drop_container(self.root);
117
1166058
        });
118
1166058
    }
119
}
120

            
121
pub struct ProtectedWriteGuard<'a, C: Markable> {
122
    reference: GcMutexGuard<'a, C>,
123

            
124
    /// Terms that have been protected during the lifetime of this guard.
125
    #[cfg(debug_assertions)]
126
    protected: RefCell<Vec<ATermRef<'static>>>,
127

            
128
    /// Symbols that have been protected during the lifetime of this guard.
129
    #[cfg(debug_assertions)]
130
    protected_symbols: RefCell<Vec<SymbolRef<'static>>>,
131
}
132

            
133
impl<'a, C: Markable> ProtectedWriteGuard<'a, C> {
134
115647291
    fn new(reference: GcMutexGuard<'a, C>) -> Self {
135
        #[cfg(debug_assertions)]
136
115647291
        return ProtectedWriteGuard {
137
115647291
            reference,
138
115647291
            protected: RefCell::new(vec![]),
139
115647291
            protected_symbols: RefCell::new(vec![]),
140
115647291
        };
141

            
142
        #[cfg(not(debug_assertions))]
143
        return ProtectedWriteGuard { reference };
144
115647291
    }
145

            
146
    /// Yields a term to insert into the container.
147
    ///
148
    /// # Safety
149
    ///
150
    /// The invariant to uphold is that the resulting term MUST be inserted into
151
    /// the container. This is checked in debug mode, but not in release mode.
152
    /// If this invariant is violated, undefined behaviour may occur during
153
    /// garbage collection.
154
79388515
    pub unsafe fn protect<'b, T: Term<'a, 'b>>(&self, term: &'b T) -> ATermRef<'static> {
155
        unsafe {
156
            // Store terms that are marked as protected to check if they are
157
            // actually in the container when the protection is dropped.
158
            #[cfg(debug_assertions)]
159
79388515
            self.protected
160
79388515
                .borrow_mut()
161
79388515
                .push(transmute::<ATermRef<'_>, ATermRef<'static>>(term.copy()));
162

            
163
79388515
            transmute::<ATermRef<'_>, ATermRef<'static>>(term.copy())
164
        }
165
79388515
    }
166

            
167
    /// Yields a symbol to insert into the container.
168
    ///
169
    /// # Safety
170
    ///
171
    /// The invariant to uphold is that the resulting symbol MUST be inserted
172
    /// into the container.
173
7769
    pub unsafe fn protect_symbol<'b, S: Symb<'a, 'b>>(&self, symbol: &'b S) -> SymbolRef<'static> {
174
        unsafe {
175
            // Store symbols that are marked as protected to check if they are
176
            // actually in the container when the protection is dropped.
177
            #[cfg(debug_assertions)]
178
7769
            self.protected_symbols
179
7769
                .borrow_mut()
180
7769
                .push(transmute::<SymbolRef<'_>, SymbolRef<'static>>(symbol.copy()));
181

            
182
7769
            transmute::<SymbolRef<'_>, SymbolRef<'static>>(symbol.copy())
183
        }
184
7769
    }
185
}
186

            
187
#[cfg(debug_assertions)]
188
impl<C: Markable> Drop for ProtectedWriteGuard<'_, C> {
189
115647291
    fn drop(&mut self) {
190
        {
191
115647291
            for term in self.protected.borrow().iter() {
192
79388515
                debug_assert!(
193
79388515
                    self.reference.contains_term(term),
194
                    "Term was protected but not actually inserted"
195
                );
196
            }
197

            
198
115647291
            for symbol in self.protected_symbols.borrow().iter() {
199
7769
                debug_assert!(
200
7769
                    self.reference.contains_symbol(symbol),
201
                    "Symbol was protected but not actually inserted"
202
                );
203
            }
204
        }
205
115647291
    }
206
}
207

            
208
impl<'a, C: Markable + Transmutable + 'a> Deref for ProtectedWriteGuard<'a, C> {
209
    type Target = C::Target<'a>;
210

            
211
69970796
    fn deref(&self) -> &Self::Target {
212
        // SAFETY: 'a is the lifetime of the underlying lock guard, which `self` borrows.
213
69970796
        unsafe { self.reference.transmute_lifetime() }
214
69970796
    }
215
}
216

            
217
impl<C: Markable + Transmutable> DerefMut for ProtectedWriteGuard<'_, C> {
218
181611810
    fn deref_mut(&mut self) -> &mut Self::Target {
219
        // SAFETY: 'a is the lifetime of the underlying lock guard, which `self` borrows.
220
181611810
        unsafe { self.reference.deref_mut().transmute_lifetime_mut() }
221
181611810
    }
222
}
223

            
224
pub struct ProtectedReadGuard<'a, C> {
225
    reference: GcMutexReadGuard<'a, C>,
226
}
227

            
228
impl<'a, C> ProtectedReadGuard<'a, C> {
229
77749309
    fn new(reference: GcMutexReadGuard<'a, C>) -> Self {
230
77749309
        Self { reference }
231
77749309
    }
232
}
233

            
234
impl<'a, C: Transmutable> Deref for ProtectedReadGuard<'a, C> {
235
    type Target = C::Target<'a>;
236

            
237
717873443
    fn deref(&self) -> &Self::Target {
238
        // SAFETY: 'a is the lifetime of the underlying lock guard, which `self` borrows.
239
717873443
        unsafe { self.reference.transmute_lifetime() }
240
717873443
    }
241
}
242

            
243
#[cfg(test)]
244
mod tests {
245
    use crate::ATerm;
246
    use crate::ATermRef;
247
    use crate::Protected;
248

            
249
    #[test]
250
1
    fn test_aterm_container() {
251
1
        merc_utilities::test_logger();
252

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

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

            
258
1000
        for _ in 0..1000 {
259
1000
            let mut write = container.write();
260
1000
            write.push(t.get());
261
1000
        }
262
1
    }
263
}