1
use std::cell::UnsafeCell;
2
use std::mem::ManuallyDrop;
3
use std::ops::Deref;
4
use std::ops::DerefMut;
5

            
6
use crate::storage::GlobalTermPoolGuard;
7
use crate::storage::THREAD_TERM_POOL;
8

            
9
/// A mutex that prevents garbage collection by holding a shared read lock on
10
/// the [super::GlobalTermPool] for the duration of the guard's lifetime.
11
/// Returns a [GcMutexGuard] on access.
12
///
13
/// # Safety
14
///
15
/// The `GcMutex` returns guards that are tied to the thread-local storage of
16
/// [crate::storage::THREAD_TERM_POOL]. This means that the guard must be
17
/// dropped before this thread-local storage is dropped. Otherwise
18
/// use-after-free will occur, which is undefined behaviour.
19
pub struct GcMutex<T> {
20
    inner: UnsafeCell<T>,
21
}
22

            
23
unsafe impl<T: Send> Send for GcMutex<T> {}
24
unsafe impl<T: Send> Sync for GcMutex<T> {}
25

            
26
impl<T> GcMutex<T> {
27
3325363
    pub fn new(value: T) -> GcMutex<T> {
28
3325363
        GcMutex {
29
3325363
            inner: UnsafeCell::new(value),
30
3325363
        }
31
3325363
    }
32

            
33
    /// Provides mutable access to the underlying value, returning a [GcMutexGuard].
34
67545785
    pub fn lock(&self) -> GcMutexGuard<'_, T> {
35
        GcMutexGuard {
36
67545785
            mutex: self,
37
            // This is only safe if the called maintains the above contract.
38
67545785
            guard: ManuallyDrop::new(THREAD_TERM_POOL.with_borrow(|tp| unsafe {
39
67545785
                std::mem::transmute::<_, GlobalTermPoolGuard<'_>>(
40
67545785
                    tp.term_pool().read_recursive().expect("Lock poisoned!"),
41
                )
42
67545785
            })),
43
        }
44
67545785
    }
45
}
46

            
47
pub struct GcMutexGuard<'a, T> {
48
    mutex: &'a GcMutex<T>,
49

            
50
    /// Only used to avoid garbage collection, will be released on drop.
51
    guard: ManuallyDrop<GlobalTermPoolGuard<'a>>,
52
}
53

            
54
impl<T> Deref for GcMutexGuard<'_, T> {
55
    type Target = T;
56

            
57
292561803
    fn deref(&self) -> &Self::Target {
58
292561803
        unsafe { &*self.mutex.inner.get() }
59
292561803
    }
60
}
61

            
62
impl<T> DerefMut for GcMutexGuard<'_, T> {
63
60760898
    fn deref_mut(&mut self) -> &mut Self::Target {
64
        // We are the only guard after `write()`, so we can provide mutable access to the underlying object.
65
60760898
        unsafe { &mut *self.mutex.inner.get() }
66
60760898
    }
67
}
68

            
69
impl<T> Drop for GcMutexGuard<'_, T> {
70
67545785
    fn drop(&mut self) {
71
67545785
        if self.guard.read_depth() == 1 {
72
            // If this is the last guard, we can trigger garbage collection when it was delayed earlier.
73
28045817
            THREAD_TERM_POOL.with_borrow(|tp| unsafe { tp.trigger_delayed_garbage_collection(&mut self.guard) })
74
39499968
        } else {
75
39499968
            // Just drop the guard
76
39499968
            unsafe { ManuallyDrop::drop(&mut self.guard) };
77
39499968
        }
78
67545785
    }
79
}