1
use std::fmt::Debug;
2
use std::pin::Pin;
3
use std::sync::Arc;
4
use std::sync::LazyLock;
5

            
6
use log::info;
7
use log::trace;
8
use parking_lot::Mutex;
9

            
10
use mcrl2_sys::atermpp::ffi;
11
use utilities::protection_set::ProtectionSet;
12

            
13
use crate::aterm::ATermRef;
14
use crate::aterm::BfTermPool;
15

            
16
use super::ATermGlobal;
17
use super::Markable;
18

            
19
/// This newtype is necessary since plain pointers cannot be marked as Send.
20
/// However since terms are immutable pointers it is fine to read them in multiple
21
/// threads.
22
#[derive(Clone, Debug)]
23
pub(crate) struct ATermPtr {
24
    pub(crate) ptr: *const ffi::_aterm,
25
}
26

            
27
impl ATermPtr {
28
302221041
    pub(crate) fn new(ptr: *const ffi::_aterm) -> ATermPtr {
29
302221041
        ATermPtr { ptr }
30
302221041
    }
31
}
32

            
33
unsafe impl Send for ATermPtr {}
34

            
35
/// The protection set for terms.
36
pub(crate) type SharedProtectionSet = Arc<BfTermPool<ProtectionSet<ATermPtr>>>;
37

            
38
/// The protection set for containers.
39
pub(crate) type SharedContainerProtectionSet = Arc<BfTermPool<ProtectionSet<Arc<dyn Markable + Sync + Send>>>>;
40

            
41
/// The single global (singleton) term pool.
42
pub(crate) struct GlobalTermPool {
43
    /// The protection set for global terms.
44
    protection_set: ProtectionSet<ATermPtr>,
45

            
46
    /// The protection sets for thread local terms.
47
    thread_protection_sets: Vec<Option<SharedProtectionSet>>,
48
    thread_container_sets: Vec<Option<SharedContainerProtectionSet>>,
49
}
50

            
51
impl GlobalTermPool {
52
410
    fn new() -> GlobalTermPool {
53
410
        // Initialise the C++ aterm library.
54
410
        ffi::initialise();
55
410

            
56
410
        // For the protection sets we disable automatic garbage collection, and call it when it is allowed.
57
410
        ffi::enable_automatic_garbage_collection(false);
58
410

            
59
410
        GlobalTermPool {
60
410
            protection_set: ProtectionSet::new(),
61
410
            thread_protection_sets: vec![],
62
410
            thread_container_sets: vec![],
63
410
        }
64
410
    }
65

            
66
    /// Protects the given aterm address and returns the term.
67
200
    pub fn protect(&mut self, term: *const ffi::_aterm) -> ATermGlobal {
68
200
        debug_assert!(!term.is_null(), "Can only protect valid terms");
69
200
        let aterm = ATermPtr::new(term);
70
200
        let root = self.protection_set.protect(aterm.clone());
71
200

            
72
200
        let term = ATermRef::new(term);
73
200
        trace!("Protected term {:?} global, index {}", term, root,);
74

            
75
200
        ATermGlobal { term, root }
76
200
    }
77

            
78
    /// Removes the [ATermGlobal] from the protection set.
79
200
    pub fn drop_term(&mut self, term: &ATermGlobal) {
80
200
        term.require_valid();
81
200

            
82
200
        trace!("Dropped term {:?} global, index {}", term.term, term.root,);
83
200
        self.protection_set.unprotect(term.root);
84
200
    }
85

            
86
    /// Register a new thread term pool to manage thread specific aspects.l
87
418
    pub(crate) fn register_thread_term_pool(&mut self) -> (SharedProtectionSet, SharedContainerProtectionSet, usize) {
88
418
        trace!("Registered ThreadTermPool {}", self.thread_protection_sets.len());
89

            
90
        // Register a protection set into the global set.
91
        // TODO: use existing free spots.
92
418
        let protection_set = Arc::new(BfTermPool::new(ProtectionSet::new()));
93
418
        self.thread_protection_sets.push(Some(protection_set.clone()));
94
418

            
95
418
        let container_protection_set = Arc::new(BfTermPool::new(ProtectionSet::new()));
96
418
        self.thread_container_sets.push(Some(container_protection_set.clone()));
97
418

            
98
418
        (
99
418
            protection_set,
100
418
            container_protection_set,
101
418
            self.thread_container_sets.len() - 1,
102
418
        )
103
418
    }
104

            
105
    /// Drops the thread term pool with the given index.
106
418
    pub(crate) fn drop_thread_term_pool(&mut self, index: usize) {
107
418
        self.thread_protection_sets[index] = None;
108
418
        self.thread_container_sets[index] = None;
109
418
        trace!("Removed ThreadTermPool {}", index);
110
418
    }
111

            
112
    /// Marks the terms in all protection sets.
113
398
    fn mark_protection_sets(&mut self, mut todo: Pin<&mut ffi::term_mark_stack>) {
114
398
        trace!("Marking terms:");
115
790
        for set in self.thread_protection_sets.iter().flatten() {
116
            // Do not lock since we acquired a global lock.
117
            unsafe {
118
790
                let protection_set = set.get();
119

            
120
2657
                for (term, root) in protection_set.iter() {
121
2657
                    ffi::aterm_mark_address(term.ptr, todo.as_mut());
122
2657

            
123
2657
                    trace!("Marked {:?}, index {root}", term.ptr);
124
                }
125
            }
126
        }
127

            
128
39807
        for (term, root) in &self.protection_set {
129
            unsafe {
130
39409
                ffi::aterm_mark_address(term.ptr, todo.as_mut());
131
39409

            
132
39409
                trace!("Marked global {:?}, index {root}", term.ptr);
133
            }
134
        }
135

            
136
790
        for set in self.thread_container_sets.iter().flatten() {
137
            // Do not lock since we acquired a global lock.
138
            unsafe {
139
790
                let protection_set = set.get();
140

            
141
790
                for (container, root) in protection_set.iter() {
142
                    container.mark(todo.as_mut());
143

            
144
                    let length = container.len();
145

            
146
                    trace!("Marked container index {root}, size {}", length);
147
                }
148
            }
149
        }
150

            
151
398
        info!("Collecting garbage \n{:?}", self);
152
398
    }
153

            
154
    /// Counts the number of terms in all protection sets.
155
398
    fn protection_set_size(&self) -> usize {
156
398
        let mut result = 0;
157
790
        for set in self.thread_protection_sets.iter().flatten() {
158
790
            result += set.read().len();
159
790
        }
160

            
161
        // Gather the sizes of all containers
162
790
        for set in self.thread_container_sets.iter().flatten() {
163
790
            for (container, _index) in set.read().iter() {
164
                result += container.len();
165
            }
166
        }
167
398
        result
168
398
    }
169

            
170
    /// Returns the number of terms in the pool.
171
404
    pub fn len(&self) -> usize {
172
404
        ffi::aterm_pool_size()
173
404
    }
174

            
175
    /// Returns the number of terms in the pool.
176
404
    pub fn capacity(&self) -> usize {
177
404
        ffi::aterm_pool_capacity()
178
404
    }
179
}
180

            
181
impl Debug for GlobalTermPool {
182
404
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183
404
        let mut protected = 0;
184
404
        let mut total = 0;
185
404
        let mut max = 0;
186

            
187
796
        for set in self.thread_protection_sets.iter().flatten() {
188
796
            let protection_set = set.read();
189
796
            protected += protection_set.len();
190
796
            total += protection_set.number_of_insertions();
191
796
            max += protection_set.maximum_size();
192
796
        }
193

            
194
404
        let mut num_containers = 0;
195
404
        let mut max_containers = 0;
196
404
        let mut total_containers = 0;
197
404
        let mut inside_containers = 0;
198
796
        for set in self.thread_container_sets.iter().flatten() {
199
796
            let protection_set = set.read();
200
796
            num_containers += protection_set.len();
201
796
            total_containers += protection_set.number_of_insertions();
202
796
            max_containers += protection_set.maximum_size();
203

            
204
796
            for (container, _) in protection_set.iter() {
205
                inside_containers += container.len();
206
            }
207
        }
208

            
209
404
        write!(f,
210
404
            "{} terms, max capacity {}, {} variables in thread root sets and {} in {} containers (term set {} insertions, max {}; container set {} insertions, max {})",
211
404
            self.len(),
212
404
            self.capacity(),
213
404
            protected,
214
404
            inside_containers,
215
404
            num_containers,
216
404
            total,
217
404
            max,
218
404
            total_containers,
219
404
            max_containers,
220
404
        )
221
404
    }
222
}
223

            
224
/// This is the global set of protection sets that are managed by the ThreadTermPool
225
410
pub(crate) static GLOBAL_TERM_POOL: LazyLock<Mutex<GlobalTermPool>> = LazyLock::new(|| Mutex::new(GlobalTermPool::new()));
226

            
227
/// Marks the terms in all protection sets using the global aterm pool.
228
398
pub(crate) fn mark_protection_sets(todo: Pin<&mut ffi::term_mark_stack>) {
229
398
    GLOBAL_TERM_POOL.lock().mark_protection_sets(todo);
230
398
}
231

            
232
/// Counts the number of terms in all protection sets.
233
398
pub(crate) fn protection_set_size() -> usize {
234
398
    GLOBAL_TERM_POOL.lock().protection_set_size()
235
398
}