1
use std::cell::UnsafeCell;
2
use std::marker::PhantomData;
3
use std::ops::Deref;
4
use std::ops::DerefMut;
5

            
6
use mcrl2_sys::atermpp::ffi;
7

            
8
/// Provides access to the mCRL2 busy forbidden protocol, where there
9
/// are thread local busy flags and one central storage for the forbidden
10
/// flags. Care must be taken to avoid deadlocks since the FFI also uses
11
/// the same flags.
12
pub struct BfTermPool<T: ?Sized> {
13
    object: UnsafeCell<T>,
14
}
15

            
16
unsafe impl<T: Send> Send for BfTermPool<T> {}
17
unsafe impl<T: Send> Sync for BfTermPool<T> {}
18

            
19
impl<T> BfTermPool<T> {
20
2615425
    pub fn new(object: T) -> BfTermPool<T> {
21
2615425
        BfTermPool {
22
2615425
            object: UnsafeCell::new(object),
23
2615425
        }
24
2615425
    }
25
}
26

            
27
impl<'a, T: ?Sized> BfTermPool<T> {
28
    /// Provides read access to the underlying object.
29
3592
    pub fn read(&'a self) -> BfTermPoolRead<'a, T> {
30
3592
        ffi::lock_shared();
31
3592
        BfTermPoolRead {
32
3592
            mutex: self,
33
3592
            _marker: Default::default(),
34
3592
        }
35
3592
    }
36

            
37
    /// Provides write access to the underlying object.
38
    pub fn write(&'a self) -> BfTermPoolWrite<'a, T> {
39
        ffi::lock_exclusive();
40

            
41
        BfTermPoolWrite {
42
            mutex: self,
43
            _marker: Default::default(),
44
        }
45
    }
46

            
47
    /// Provides read access to the underlying object.
48
    ///
49
    /// # Safety
50
    ///
51
    /// Assumes that we are in an exclusive section.
52
246259839
    pub unsafe fn get(&'a self) -> &'a T {
53
246259839
        unsafe { &*self.object.get() }
54
246259839
    }
55

            
56
    /// Provides write access to the underlying object
57
    ///
58
    /// # Safety
59
    ///
60
    /// Provides mutable access given that other threads use [write] and [read]
61
    /// exclusively. If we are already in an exclusive context then lock can be
62
    /// set to false.
63
912239060
    pub unsafe fn write_exclusive(&'a self) -> BfTermPoolThreadWrite<'a, T> {
64
912239060
        // This is a lock shared, but assuming that only ONE thread uses this function.
65
912239060
        ffi::lock_shared();
66
912239060

            
67
912239060
        BfTermPoolThreadWrite {
68
912239060
            mutex: self,
69
912239060
            locked: true,
70
912239060
            _marker: Default::default(),
71
912239060
        }
72
912239060
    }
73
}
74

            
75
pub struct BfTermPoolRead<'a, T: ?Sized> {
76
    mutex: &'a BfTermPool<T>,
77
    _marker: PhantomData<&'a ()>,
78
}
79

            
80
impl<T: ?Sized> Deref for BfTermPoolRead<'_, T> {
81
    type Target = T;
82

            
83
7572
    fn deref(&self) -> &Self::Target {
84
7572
        // There can only be read guards.
85
7572
        unsafe { &*self.mutex.object.get() }
86
7572
    }
87
}
88

            
89
impl<T: ?Sized> Drop for BfTermPoolRead<'_, T> {
90
3592
    fn drop(&mut self) {
91
3592
        // If we leave the shared section and the counter is zero.
92
3592
        ffi::unlock_shared();
93
3592
    }
94
}
95

            
96
pub struct BfTermPoolWrite<'a, T: ?Sized> {
97
    mutex: &'a BfTermPool<T>,
98
    _marker: PhantomData<&'a ()>,
99
}
100

            
101
impl<T: ?Sized> Deref for BfTermPoolWrite<'_, T> {
102
    type Target = T;
103

            
104
    fn deref(&self) -> &Self::Target {
105
        // There can only be read guards.
106
        unsafe { &*self.mutex.object.get() }
107
    }
108
}
109

            
110
impl<T: ?Sized> DerefMut for BfTermPoolWrite<'_, T> {
111
    fn deref_mut(&mut self) -> &mut Self::Target {
112
        // We are the only guard after `write()`, so we can provide mutable access to the underlying object.
113
        unsafe { &mut *self.mutex.object.get() }
114
    }
115
}
116

            
117
impl<T: ?Sized> Drop for BfTermPoolWrite<'_, T> {
118
    fn drop(&mut self) {
119
        ffi::unlock_exclusive();
120
    }
121
}
122

            
123
pub struct BfTermPoolThreadWrite<'a, T: ?Sized> {
124
    mutex: &'a BfTermPool<T>,
125
    locked: bool,
126
    _marker: PhantomData<&'a ()>,
127
}
128

            
129
impl<T: ?Sized> BfTermPoolThreadWrite<'_, T> {
130
    /// Unlocks the guard prematurely, but returns whether the shared section was actually left.
131
302220841
    pub fn unlock(&mut self) -> bool {
132
302220841
        if self.locked {
133
302220841
            self.locked = false;
134
302220841
            ffi::unlock_shared()
135
        } else {
136
            false
137
        }
138
302220841
    }
139
}
140

            
141
impl<T: ?Sized> Deref for BfTermPoolThreadWrite<'_, T> {
142
    type Target = T;
143

            
144
443316619
    fn deref(&self) -> &Self::Target {
145
443316619
        // There can only be read guards.
146
443316619
        unsafe { &*self.mutex.object.get() }
147
443316619
    }
148
}
149

            
150
impl<T: ?Sized> DerefMut for BfTermPoolThreadWrite<'_, T> {
151
1055995763
    fn deref_mut(&mut self) -> &mut Self::Target {
152
1055995763
        // We are the only guard after `write()`, so we can provide mutable access to the underlying object.
153
1055995763
        unsafe { &mut *self.mutex.object.get() }
154
1055995763
    }
155
}
156

            
157
impl<T: ?Sized> Drop for BfTermPoolThreadWrite<'_, T> {
158
912239060
    fn drop(&mut self) {
159
912239060
        if self.locked {
160
610018219
            ffi::unlock_shared();
161
610018219
        }
162
912239060
    }
163
}