1
use std::cell::Cell;
2
use std::cell::RefCell;
3
use std::cell::UnsafeCell;
4
use std::mem::ManuallyDrop;
5
use std::ops::Deref;
6
use std::ops::DerefMut;
7
use std::sync::Arc;
8
use std::sync::Mutex;
9

            
10
use log::debug;
11

            
12
use merc_pest_consume::Parser;
13
use merc_sharedmutex::RecursiveLock;
14
use merc_sharedmutex::RecursiveLockReadGuard;
15
use merc_unsafety::ProtectionIndex;
16
use merc_unsafety::ProtectionSet;
17
use merc_unsafety::StablePointer;
18
use merc_utilities::MercError;
19
use merc_utilities::debug_trace;
20

            
21
use crate::ATermIndex;
22
use crate::Markable;
23
use crate::Return;
24
use crate::Rule;
25
use crate::Symb;
26
use crate::Symbol;
27
use crate::SymbolRef;
28
use crate::Term;
29
use crate::TermParser;
30
use crate::aterm::ATerm;
31
use crate::aterm::ATermRef;
32
use crate::storage::AGGRESSIVE_GC;
33
use crate::storage::GlobalTermPool;
34
use crate::storage::GlobalTermPoolGuard;
35
use crate::storage::SharedTerm;
36
use crate::storage::SharedTermProtection;
37
use crate::storage::global_aterm_pool::GLOBAL_TERM_POOL;
38

            
39
thread_local! {
40
    /// Thread-specific [ThreadTermPool] that manages protection sets for the current thread.
41
    ///
42
    /// Deliberately not wrapped in a `RefCell`: term construction hands out `Return` values
43
    /// whose recursive read guard points into this pool, invisibly to a `RefCell` borrow
44
    /// counter. Obtaining `&mut ThreadTermPool` (e.g. via `with_borrow_mut`) while such a
45
    /// guard is alive would invalidate the guard's reference, so no `&mut` access may exist
46
    /// at all; all methods take `&self` and use interior mutability where needed.
47
    pub static THREAD_TERM_POOL: ThreadTermPool = ThreadTermPool::new();
48
}
49

            
50
/// Per-thread term pool managing local protection sets for interaction with the [GlobalTermPool].
51
pub struct ThreadTermPool {
52
    /// Contains all the protection sets for this thread.
53
    protection_sets: Arc<UnsafeCell<SharedTermProtection>>,
54

            
55
    /// A separate protection set for sendable terms, see [crate::ATermSend].
56
    send_term_protection_set: Arc<Mutex<ProtectionSet<ATermIndex>>>,
57

            
58
    /// The number of times terms have been created before garbage collection is triggered.
59
    garbage_collection_counter: Cell<usize>,
60

            
61
    /// A vector of terms that are used to store the arguments of a term for lookup.
62
    tmp_arguments: RefCell<Vec<ATermRef<'static>>>,
63

            
64
    /// A local view for the global term pool.
65
    term_pool: RecursiveLock<GlobalTermPool>,
66

            
67
    /// Copy of the default terms since thread local access is cheaper.
68
    int_symbol: SymbolRef<'static>,
69
    empty_list_symbol: SymbolRef<'static>,
70
    list_symbol: SymbolRef<'static>,
71
}
72

            
73
impl ThreadTermPool {
74
    /// Creates a new thread-local term pool.
75
1768
    fn new() -> Self {
76
        // Register protection sets with global pool
77
1768
        let term_pool: RecursiveLock<GlobalTermPool> = RecursiveLock::from_mutex(GLOBAL_TERM_POOL.share());
78

            
79
1768
        let mut pool = term_pool.write().expect("Lock poisoned!");
80

            
81
1768
        let (protection_sets, send_term_protection_set) = pool.register_thread_term_pool();
82
1768
        let int_symbol = pool.get_int_symbol().copy();
83
1768
        let empty_list_symbol = pool.get_empty_list_symbol().copy();
84
1768
        let list_symbol = pool.get_list_symbol().copy();
85
1768
        drop(pool);
86

            
87
        // Arbitrary value to trigger garbage collection
88
        Self {
89
1768
            protection_sets,
90
1768
            send_term_protection_set,
91
1768
            garbage_collection_counter: Cell::new(if AGGRESSIVE_GC { 1 } else { 1000 }),
92
1768
            tmp_arguments: RefCell::new(Vec::new()),
93
1768
            int_symbol,
94
1768
            empty_list_symbol,
95
1768
            list_symbol,
96
1768
            term_pool,
97
        }
98
1768
    }
99

            
100
    /// Creates a constant [ATerm] (arity 0) for the given symbol.
101
5642209
    pub fn create_constant<'a, 'b, S: Symb<'a, 'b>>(&self, symbol: &'b S) -> ATerm {
102
5642209
        assert!(symbol.arity() == 0, "A constant should not have arity > 0");
103

            
104
5642209
        let empty_args: [ATermRef<'_>; 0] = [];
105
5642209
        let guard = self.term_pool.read_recursive().expect("Lock poisoned!");
106

            
107
5642209
        let (index, inserted) = guard.create_term_array(symbol, &empty_args);
108
5642209
        let result = self.protect_guard(guard, &unsafe { ATermRef::from_index(&index) });
109

            
110
5642209
        if inserted {
111
282346
            // Intentially called after the guard is dropped.
112
282346
            self.decrement_garbage_collection_counter();
113
5359863
        }
114

            
115
5642209
        result
116
5642209
    }
117

            
118
    /// Create a term with the given arguments
119
2269747
    pub fn create_term<'a, 'b, S: Symb<'a, 'b>, T: Term<'a, 'b>>(
120
2269747
        &self,
121
2269747
        symbol: &'b S,
122
2269747
        args: &'b [T],
123
2269747
    ) -> Return<ATermRef<'static>> {
124
        // We cannot perform garbage collection afterwards since the guard is alive in the return.
125
2269747
        self.trigger_garbage_collection();
126

            
127
2269747
        let guard = self.term_pool.read_recursive().expect("Lock poisoned!");
128
2269747
        let mut arguments = self.tmp_arguments.borrow_mut();
129

            
130
2269747
        arguments.clear();
131
4321948
        for arg in args {
132
4321948
            unsafe {
133
4321948
                arguments.push(ATermRef::from_index(arg.shared()));
134
4321948
            }
135
        }
136

            
137
2269747
        let (index, inserted) = guard.create_term_array(symbol, &arguments);
138
2269747
        let result = self.make_return(index, guard);
139

            
140
2269747
        if inserted {
141
491895
            self.decrement_garbage_collection_counter();
142
1806223
        }
143

            
144
2269747
        result
145
2269747
    }
146

            
147
    /// Create a term with the given index.
148
9250344
    pub fn create_int(&self, value: usize) -> ATerm {
149
9250344
        let guard = self.term_pool.read_recursive().expect("Lock poisoned!");
150
9250344
        let (index, inserted) = guard.create_int(value);
151
9250344
        let result = self.protect_guard(guard, &unsafe { ATermRef::from_index(&index) });
152

            
153
9250344
        if inserted {
154
7661619
            // Intentially called after the guard is dropped.
155
7661619
            self.decrement_garbage_collection_counter();
156
7661619
        }
157

            
158
9250344
        result
159
9250344
    }
160

            
161
    /// Create a term with the given arguments given by the iterator.
162
    ///
163
    /// # Panics
164
    ///
165
    /// The iterator is driven while an internal argument buffer is borrowed, so an
166
    /// iterator that itself constructs terms (e.g. through [ATerm::with_args] or
167
    /// [crate::ATerm::with_iter]) panics with a `RefCell` double borrow.
168
263553
    pub fn create_term_iter<'a, 'b, 'c, 'd, S, I, T>(&self, symbol: &'b S, args: I) -> ATerm
169
263553
    where
170
263553
        S: Symb<'a, 'b>,
171
263553
        I: IntoIterator<Item = T>,
172
263553
        T: Term<'c, 'd>,
173
    {
174
263553
        let guard = self.term_pool.read_recursive().expect("Lock poisoned!");
175
263553
        let mut arguments = self.tmp_arguments.borrow_mut();
176
263553
        arguments.clear();
177
508831
        for arg in args {
178
508831
            unsafe {
179
508831
                arguments.push(ATermRef::from_index(arg.shared()));
180
508831
            }
181
        }
182

            
183
263553
        let (index, inserted) = guard.create_term_array(symbol, &arguments);
184

            
185
263553
        let result = self.protect_guard(guard, &unsafe { ATermRef::from_index(&index) });
186

            
187
263553
        if inserted {
188
112162
            // Intentially called after the guard is dropped.
189
112162
            self.decrement_garbage_collection_counter();
190
151395
        }
191

            
192
263553
        result
193
263553
    }
194

            
195
    /// Create a term with the given arguments given by the iterator that is fallible.
196
    ///
197
    /// # Panics
198
    ///
199
    /// The iterator is driven while an internal argument buffer is borrowed, so an
200
    /// iterator that itself constructs terms panics with a `RefCell` double borrow;
201
    /// see [Self::create_term_iter].
202
211056
    pub fn try_create_term_iter<'a, 'b, 'c, 'd, S, I, T>(&self, symbol: &'b S, args: I) -> Result<ATerm, MercError>
203
211056
    where
204
211056
        S: Symb<'a, 'b>,
205
211056
        I: IntoIterator<Item = Result<T, MercError>>,
206
211056
        T: Term<'c, 'd>,
207
    {
208
211056
        let guard = self.term_pool.read_recursive().expect("Lock poisoned!");
209
211056
        let mut arguments = self.tmp_arguments.borrow_mut();
210
211056
        arguments.clear();
211
217122
        for arg in args {
212
            unsafe {
213
217117
                arguments.push(ATermRef::from_index(arg?.shared()));
214
            }
215
        }
216

            
217
211056
        let (index, inserted) = guard.create_term_array(symbol, &arguments);
218
211056
        let result = Ok(self.protect_guard(guard, &unsafe { ATermRef::from_index(&index) }));
219

            
220
211056
        if inserted {
221
4841
            // Intentially called after the guard is dropped.
222
4841
            self.decrement_garbage_collection_counter();
223
207125
        }
224

            
225
211056
        result
226
211056
    }
227

            
228
    /// Create a term with the given arguments given by the iterator.
229
    ///
230
    /// # Panics
231
    ///
232
    /// The iterator is driven while an internal argument buffer is borrowed, so an
233
    /// iterator that itself constructs terms panics with a `RefCell` double borrow;
234
    /// see [Self::create_term_iter].
235
12836231
    pub fn create_term_iter_head<'a, 'b, 'c, 'd, 'e, 'f, S, H, I, T>(
236
12836231
        &self,
237
12836231
        symbol: &'b S,
238
12836231
        head: &'d H,
239
12836231
        args: I,
240
12836231
    ) -> ATerm
241
12836231
    where
242
12836231
        S: Symb<'a, 'b>,
243
12836231
        H: Term<'c, 'd>,
244
12836231
        I: IntoIterator<Item = T>,
245
12836231
        T: Term<'e, 'f>,
246
    {
247
12836231
        let guard = self.term_pool.read_recursive().expect("Lock poisoned!");
248
12836231
        let mut arguments = self.tmp_arguments.borrow_mut();
249
12836231
        arguments.clear();
250
12836231
        unsafe {
251
12836231
            arguments.push(ATermRef::from_index(head.shared()));
252
12836231
        }
253
20267930
        for arg in args {
254
20267930
            unsafe {
255
20267930
                arguments.push(ATermRef::from_index(arg.shared()));
256
20267930
            }
257
        }
258

            
259
12836231
        let (index, inserted) = guard.create_term_array(symbol, &arguments);
260

            
261
12836231
        let result = self.protect_guard(guard, &unsafe { ATermRef::from_index(&index) });
262

            
263
12836231
        if inserted {
264
1195884
            // Intentially called after the guard is dropped.
265
1195884
            self.decrement_garbage_collection_counter();
266
11640347
        }
267

            
268
12836231
        result
269
12836231
    }
270

            
271
    /// Create a function symbol
272
6609466
    pub fn create_symbol<N: Into<String> + AsRef<str>>(&self, name: N, arity: usize) -> Symbol {
273
6609466
        self.term_pool
274
6609466
            .read_recursive()
275
6609466
            .expect("Lock poisoned!")
276
6609466
            .create_symbol(name, arity, |index| unsafe {
277
6609466
                self.protect_symbol(&SymbolRef::from_index(&index))
278
6609466
            })
279
6609466
    }
280

            
281
    /// Protect the term by adding its index to the protection set
282
813921460
    pub fn protect(&self, term: &ATermRef<'_>) -> ATerm {
283
        // Protect the term by adding its index to the protection set
284
813921460
        let root = self
285
813921460
            .lock_protection_set()
286
813921460
            .term_protection_set
287
813921460
            .protect(unsafe { term.shared().copy() });
288

            
289
        // Return the protected term
290
813921460
        let result = ATerm::from_index(term.shared(), root);
291

            
292
813921460
        debug_trace!(
293
            "Protected term {:?}, root {}, protection set {}",
294
            term,
295
            root,
296
            self.index()
297
        );
298

            
299
813921460
        result
300
813921460
    }
301

            
302
    /// Protect the term by adding its index to the protection set
303
118385983
    pub fn protect_guard(&self, _guard: RecursiveLockReadGuard<'_, GlobalTermPool>, term: &ATermRef<'_>) -> ATerm {
304
        // Protect the term by adding its index to the protection set
305
        // SAFETY: If the global term pool is locked, so we can safely access the protection set.
306
        // Copying the index is justified as in `protect`: `term` is alive for this call and
307
        // the protection set keeps it a GC root afterwards.
308
118385983
        let root = unsafe {
309
118385983
            (*self.protection_sets.get())
310
118385983
                .term_protection_set
311
118385983
                .protect(term.shared().copy())
312
        };
313

            
314
        // Return the protected term
315
118385983
        let result = ATerm::from_index(term.shared(), root);
316

            
317
118385983
        debug_trace!(
318
            "Protected term {:?}, root {}, protection set {}",
319
            term,
320
            root,
321
            self.index()
322
        );
323

            
324
118385983
        result
325
118385983
    }
326

            
327
    /// Unprotects a term from this thread's protection set.
328
932307443
    pub fn drop(&self, term: &ATerm) {
329
        // SAFETY: `term.root()` was returned by a matching `protect` and the
330
        // owning `ATerm` is dropped exactly once, so it is unprotected once.
331
932307443
        unsafe {
332
932307443
            self.lock_protection_set().term_protection_set.unprotect(term.root());
333
932307443
        }
334

            
335
932307443
        debug_trace!(
336
            "Unprotected term {:?}, root {}, protection set {}",
337
            term,
338
            term.root(),
339
            self.index()
340
        );
341
932307443
    }
342

            
343
    /// Protects a container in this thread's container protection set.
344
8939820
    pub fn protect_container(&self, container: Arc<dyn Markable + Send + Sync>) -> ProtectionIndex {
345
8939820
        let root = self.lock_protection_set().container_protection_set.protect(container);
346

            
347
8939820
        debug_trace!("Protected container index {}, protection set {}", root, self.index());
348

            
349
8939820
        root
350
8939820
    }
351

            
352
    /// Unprotects a container from this thread's container protection set.
353
8939820
    pub fn drop_container(&self, root: ProtectionIndex) {
354
        // SAFETY: `root` was returned by a matching `protect_container` and the
355
        // owning handle is dropped exactly once, so it is unprotected once.
356
8939820
        unsafe {
357
8939820
            self.lock_protection_set().container_protection_set.unprotect(root);
358
8939820
        }
359

            
360
8939820
        debug_trace!("Unprotected container index {}, protection set {}", root, self.index());
361
8939820
    }
362

            
363
    /// Parse the given string and returns the Term representation.
364
11433
    pub fn from_string(&self, text: &str) -> Result<ATerm, MercError> {
365
11433
        let mut result = TermParser::parse(Rule::TermSpec, text)?;
366
11433
        let root = result.next().unwrap();
367

            
368
11433
        Ok(TermParser::TermSpec(root).unwrap())
369
11433
    }
370

            
371
    /// Protects a symbol from garbage collection.
372
6788112
    pub fn protect_symbol(&self, symbol: &SymbolRef<'_>) -> Symbol {
373
6788112
        let mut lock = self.lock_protection_set();
374
        // Once inserted the protection set makes it a GC root until the
375
        // returned `Symbol` unprotects it.
376
6788112
        let root = lock.symbol_protection_set.protect(unsafe { symbol.shared().copy() });
377
6788112
        let result = unsafe { Symbol::from_index(symbol.shared(), root) };
378

            
379
6788112
        debug_trace!(
380
            "Protected symbol {}, root {}, protection set {}",
381
            symbol,
382
            result.root(),
383
            lock.index,
384
        );
385

            
386
6788112
        result
387
6788112
    }
388

            
389
    /// Unprotects a symbol, allowing it to be garbage collected.
390
6761616
    pub fn drop_symbol(&self, symbol: &mut Symbol) {
391
        // SAFETY: `symbol.root()` was returned by a matching `protect_symbol`
392
        // and the owning `Symbol` is dropped exactly once, so it is
393
        // unprotected once.
394
6761616
        unsafe {
395
6761616
            self.lock_protection_set()
396
6761616
                .symbol_protection_set
397
6761616
                .unprotect(symbol.root());
398
6761616
        }
399
6761616
    }
400

            
401
    /// Returns the symbol for ATermInt
402
1586143846
    pub fn int_symbol(&self) -> &SymbolRef<'_> {
403
1586143846
        &self.int_symbol
404
1586143846
    }
405

            
406
    /// Returns the symbol for ATermList
407
2829957
    pub fn list_symbol(&self) -> &SymbolRef<'_> {
408
2829957
        &self.list_symbol
409
2829957
    }
410

            
411
    /// Returns the symbol for the empty ATermInt
412
2868223
    pub fn empty_list_symbol(&self) -> &SymbolRef<'_> {
413
2868223
        &self.empty_list_symbol
414
2868223
    }
415

            
416
    /// Enables or disables automatic garbage collection.
417
    pub fn automatic_garbage_collection(&self, enabled: bool) {
418
        let mut guard = self.term_pool.write().expect("Lock poisoned!");
419
        guard.automatic_garbage_collection(enabled);
420
    }
421

            
422
    /// Forces a garbage collection to occur, regardless of the current counter value or whether it is enabled.
423
71
    pub fn force_collect_garbage(&self) {
424
71
        let mut guard = self.term_pool.write().expect("Lock poisoned!");
425
71
        guard.collect_garbage();
426
71
    }
427

            
428
    /// Perform a garbage collection.
429
558159
    pub fn collect_garbage(&self) {
430
558159
        if !self.term_pool.is_locked() {
431
            // Trigger garbage collection and acquire a new counter value.
432
558159
            if let Some(mut guard) = self.term_pool.try_write().expect("Lock poisoned!") {
433
558159
                let value = guard.trigger_garbage_collection();
434
558159
                self.garbage_collection_counter.set(value);
435
558159
            }
436
        }
437
558159
    }
438

            
439
    /// Triggers delayed garbage collection if the counter has reached zero.
440
    ///
441
    /// # Safety
442
    ///
443
    /// This function drops the passed guard.
444
643412316
    pub(crate) unsafe fn trigger_delayed_garbage_collection(&self, guard: &mut ManuallyDrop<GlobalTermPoolGuard<'_>>) {
445
        // Read the depth before dropping the guard; using `guard` after `ManuallyDrop::drop`
446
        // would violate its contract. The guard itself accounts for one level.
447
643412316
        debug_assert!(
448
643412316
            guard.read_depth() == 1,
449
            "Cannot trigger garbage collection while holding another read lock"
450
        );
451

            
452
643412316
        unsafe {
453
643412316
            ManuallyDrop::drop(guard);
454
643412316
        }
455
643412316
        if self.garbage_collection_counter.get() == 0 {
456
88297
            self.trigger_garbage_collection();
457
643324019
        }
458
643412316
    }
459

            
460
    /// Decrements the garbage collection counter and triggers garbage collection if necessary.
461
19992851
    fn decrement_garbage_collection_counter(&self) {
462
        // If the term was newly inserted, decrease the garbage collection counter and trigger garbage collection if necessary
463
19992851
        self.garbage_collection_counter
464
19992851
            .set(self.garbage_collection_counter.get().saturating_sub(1));
465

            
466
19992851
        self.trigger_garbage_collection();
467
19992851
    }
468

            
469
    /// Triggers garbage collection if the counter has reached zero.
470
25943551
    fn trigger_garbage_collection(&self) {
471
25943551
        if self.garbage_collection_counter.get() == 0 && !self.term_pool.is_locked() {
472
552952
            self.collect_garbage();
473
25390599
        }
474
25943551
    }
475

            
476
    /// Returns a reference to the send term protection set.
477
200025
    pub fn send_term_protection_set(&self) -> &Arc<Mutex<ProtectionSet<ATermIndex>>> {
478
200025
        &self.send_term_protection_set
479
200025
    }
480

            
481
    /// Returns a reference to the global term pool.
482
1538624113
    pub(crate) fn term_pool(&self) -> &RecursiveLock<GlobalTermPool> {
483
1538624113
        &self.term_pool
484
1538624113
    }
485

            
486
    /// Replace the entry in the protection set with the given term.
487
    pub(crate) fn replace(
488
        &self,
489
        _guard: RecursiveLockReadGuard<'_, GlobalTermPool>,
490
        root: ProtectionIndex,
491
        term: StablePointer<SharedTerm>,
492
    ) {
493
        // Protect the term by adding its index to the protection set
494
        // SAFETY: If the global term pool is locked, so we can safely access the protection set.
495
        unsafe { &mut *self.protection_sets.get() }
496
            .term_protection_set
497
            .replace(root, term);
498
    }
499

            
500
    /// Creates a Return for the given index and guard.
501
5862402
    fn make_return(
502
5862402
        &self,
503
5862402
        index: ATermIndex,
504
5862402
        guard: RecursiveLockReadGuard<'_, GlobalTermPool>,
505
5862402
    ) -> Return<ATermRef<'static>> {
506
        // SAFETY: The guard is guaranteed to live as long as the returned term, since it is thread local and Return cannot be sent to other threads.
507
        unsafe {
508
5862402
            Return::new(
509
5862402
                std::mem::transmute::<RecursiveLockReadGuard<'_, _>, RecursiveLockReadGuard<'static, _>>(guard),
510
5862402
                ATermRef::from_index(&index),
511
            )
512
        }
513
5862402
    }
514

            
515
    /// Returns the index of the protection set.
516
1768
    fn index(&self) -> usize {
517
1768
        self.lock_protection_set().index
518
1768
    }
519

            
520
    /// The protection set is locked by the global read-write lock
521
1777660045
    fn lock_protection_set(&self) -> ProtectionSetGuard<'_> {
522
1777660045
        let guard = self.term_pool.read_recursive().expect("Lock poisoned!");
523
1777660045
        let protection_set = unsafe { &mut *self.protection_sets.get() };
524

            
525
1777660045
        ProtectionSetGuard::new(guard, protection_set)
526
1777660045
    }
527
}
528

            
529
impl Drop for ThreadTermPool {
530
1768
    fn drop(&mut self) {
531
1768
        let mut write = self.term_pool.write().expect("Lock poisoned!");
532

            
533
1768
        debug!("{}", write.metrics());
534
1768
        write.deregister_thread_pool(self.index());
535

            
536
1768
        debug!("{}", unsafe { &mut *self.protection_sets.get() }.metrics());
537
1768
        debug!(
538
            "Acquired {} read locks and {} write locks",
539
            self.term_pool.read_recursive_call_count(),
540
            self.term_pool.write_call_count()
541
        )
542
1768
    }
543
}
544

            
545
struct ProtectionSetGuard<'a> {
546
    _guard: RecursiveLockReadGuard<'a, GlobalTermPool>,
547
    object: &'a mut SharedTermProtection,
548
}
549

            
550
impl ProtectionSetGuard<'_> {
551
1777660045
    fn new<'a>(
552
1777660045
        guard: RecursiveLockReadGuard<'a, GlobalTermPool>,
553
1777660045
        object: &'a mut SharedTermProtection,
554
1777660045
    ) -> ProtectionSetGuard<'a> {
555
1777660045
        ProtectionSetGuard { _guard: guard, object }
556
1777660045
    }
557
}
558

            
559
impl Deref for ProtectionSetGuard<'_> {
560
    type Target = SharedTermProtection;
561

            
562
1774
    fn deref(&self) -> &Self::Target {
563
1774
        self.object
564
1774
    }
565
}
566

            
567
impl DerefMut for ProtectionSetGuard<'_> {
568
1777658271
    fn deref_mut(&mut self) -> &mut Self::Target {
569
1777658271
        self.object
570
1777658271
    }
571
}
572

            
573
#[cfg(test)]
574
mod tests {
575
    use crate::ATerm;
576
    use crate::Symb;
577
    use crate::Symbol;
578
    use crate::Term;
579
    use crate::storage::THREAD_TERM_POOL;
580

            
581
    use std::thread;
582

            
583
    #[test]
584
1
    fn test_thread_local_protection() {
585
1
        merc_utilities::test_logger();
586

            
587
1
        thread::scope(|scope| {
588
1
            for _ in 0..3 {
589
3
                scope.spawn(|| {
590
                    // Create and protect some terms
591
3
                    let symbol = Symbol::new("test", 0);
592
3
                    let term = ATerm::constant(&symbol);
593
3
                    let protected = term.protect();
594

            
595
                    // Verify protection
596
3
                    THREAD_TERM_POOL.with(|tp| {
597
3
                        assert!(
598
3
                            tp.lock_protection_set()
599
3
                                .term_protection_set
600
3
                                .contains_root(protected.root())
601
                        );
602
3
                    });
603

            
604
                    // Unprotect
605
3
                    let root = protected.root();
606
3
                    drop(protected);
607

            
608
3
                    THREAD_TERM_POOL.with(|tp| {
609
3
                        assert!(!tp.lock_protection_set().term_protection_set.contains_root(root));
610
3
                    });
611
3
                });
612
            }
613
1
        });
614
1
    }
615

            
616
    #[test]
617
1
    fn test_parsing() {
618
1
        merc_utilities::test_logger();
619

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

            
622
1
        assert!(t.get_head_symbol().name() == "f");
623
1
        assert!(t.arg(0).get_head_symbol().name() == "g");
624
1
        assert!(t.arg(1).get_head_symbol().name() == "b");
625
1
    }
626

            
627
    #[test]
628
1
    fn test_create_term() {
629
1
        merc_utilities::test_logger();
630

            
631
1
        let f = Symbol::new("f", 2);
632
1
        let g = Symbol::new("g", 1);
633

            
634
1
        let t = THREAD_TERM_POOL.with(|tp| {
635
1
            tp.create_term(
636
1
                &f,
637
1
                &[
638
1
                    tp.create_term(&g, &[tp.create_constant(&Symbol::new("a", 0))])
639
1
                        .protect(),
640
1
                    tp.create_constant(&Symbol::new("b", 0)),
641
1
                ],
642
1
            )
643
1
            .protect()
644
1
        });
645

            
646
1
        assert!(t.get_head_symbol().name() == "f");
647
1
        assert!(t.arg(0).get_head_symbol().name() == "g");
648
1
        assert!(t.arg(1).get_head_symbol().name() == "b");
649
1
    }
650
}