Coverage Report

Created: 2026-07-30 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/crossbeam-epoch-0.9.20/src/collector.rs
Line
Count
Source
1
/// Epoch-based garbage collector.
2
///
3
/// # Examples
4
///
5
/// ```
6
/// use crossbeam_epoch::Collector;
7
///
8
/// let collector = Collector::new();
9
///
10
/// let handle = collector.register();
11
/// drop(collector); // `handle` still works after dropping `collector`
12
///
13
/// handle.pin().flush();
14
/// ```
15
use core::fmt;
16
17
use crate::guard::Guard;
18
use crate::internal::{Global, Local};
19
use crate::primitive::sync::Arc;
20
21
/// An epoch-based garbage collector.
22
pub struct Collector {
23
    pub(crate) global: Arc<Global>,
24
}
25
26
unsafe impl Send for Collector {}
27
unsafe impl Sync for Collector {}
28
29
impl Default for Collector {
30
0
    fn default() -> Self {
31
0
        Self {
32
0
            global: Arc::new(Global::new()),
33
0
        }
34
0
    }
35
}
36
37
impl Collector {
38
    /// Creates a new collector.
39
0
    pub fn new() -> Self {
40
0
        Self::default()
41
0
    }
42
43
    /// Registers a new handle for the collector.
44
0
    pub fn register(&self) -> LocalHandle {
45
0
        Local::register(self)
46
0
    }
47
}
48
49
impl Clone for Collector {
50
    /// Creates another reference to the same garbage collector.
51
0
    fn clone(&self) -> Self {
52
0
        Collector {
53
0
            global: self.global.clone(),
54
0
        }
55
0
    }
56
}
57
58
impl fmt::Debug for Collector {
59
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60
0
        f.pad("Collector { .. }")
61
0
    }
62
}
63
64
impl PartialEq for Collector {
65
    /// Checks if both handles point to the same collector.
66
0
    fn eq(&self, rhs: &Collector) -> bool {
67
0
        Arc::ptr_eq(&self.global, &rhs.global)
68
0
    }
69
}
70
impl Eq for Collector {}
71
72
/// A handle to a garbage collector.
73
pub struct LocalHandle {
74
    pub(crate) local: *const Local,
75
}
76
77
impl LocalHandle {
78
    /// Pins the handle.
79
    #[inline]
80
0
    pub fn pin(&self) -> Guard {
81
0
        unsafe { (*self.local).pin() }
82
0
    }
Unexecuted instantiation: <crossbeam_epoch::collector::LocalHandle>::pin
Unexecuted instantiation: <crossbeam_epoch::collector::LocalHandle>::pin
83
84
    /// Returns `true` if the handle is pinned.
85
    #[inline]
86
0
    pub fn is_pinned(&self) -> bool {
87
0
        unsafe { (*self.local).is_pinned() }
88
0
    }
Unexecuted instantiation: <crossbeam_epoch::collector::LocalHandle>::is_pinned
Unexecuted instantiation: <crossbeam_epoch::collector::LocalHandle>::is_pinned
89
90
    /// Returns the `Collector` associated with this handle.
91
    #[inline]
92
0
    pub fn collector(&self) -> &Collector {
93
0
        unsafe { (*self.local).collector() }
94
0
    }
95
}
96
97
impl Drop for LocalHandle {
98
    #[inline]
99
0
    fn drop(&mut self) {
100
0
        unsafe {
101
0
            Local::release_handle(&*self.local);
102
0
        }
103
0
    }
Unexecuted instantiation: <crossbeam_epoch::collector::LocalHandle as core::ops::drop::Drop>::drop
Unexecuted instantiation: <crossbeam_epoch::collector::LocalHandle as core::ops::drop::Drop>::drop
104
}
105
106
impl fmt::Debug for LocalHandle {
107
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108
0
        f.pad("LocalHandle { .. }")
109
0
    }
110
}
111
112
#[cfg(all(test, not(crossbeam_loom)))]
113
mod tests {
114
    use std::mem::ManuallyDrop;
115
    use std::sync::atomic::{AtomicUsize, Ordering};
116
    use std::vec::Vec;
117
118
    use crossbeam_utils::thread;
119
120
    use crate::{Collector, Owned};
121
122
    const NUM_THREADS: usize = 8;
123
124
    #[test]
125
    fn pin_reentrant() {
126
        let collector = Collector::new();
127
        let handle = collector.register();
128
        drop(collector);
129
130
        assert!(!handle.is_pinned());
131
        {
132
            let _guard = &handle.pin();
133
            assert!(handle.is_pinned());
134
            {
135
                let _guard = &handle.pin();
136
                assert!(handle.is_pinned());
137
            }
138
            assert!(handle.is_pinned());
139
        }
140
        assert!(!handle.is_pinned());
141
    }
142
143
    #[test]
144
    fn flush_local_bag() {
145
        let collector = Collector::new();
146
        let handle = collector.register();
147
        drop(collector);
148
149
        for _ in 0..100 {
150
            let guard = &handle.pin();
151
            unsafe {
152
                let a = Owned::new(7).into_shared(guard);
153
                guard.defer_destroy(a);
154
155
                assert!(!(*guard.local).bag.with(|b| (*b).is_empty()));
156
157
                while !(*guard.local).bag.with(|b| (*b).is_empty()) {
158
                    guard.flush();
159
                }
160
            }
161
        }
162
    }
163
164
    #[test]
165
    fn garbage_buffering() {
166
        let collector = Collector::new();
167
        let handle = collector.register();
168
        drop(collector);
169
170
        let guard = &handle.pin();
171
        unsafe {
172
            for _ in 0..10 {
173
                let a = Owned::new(7).into_shared(guard);
174
                guard.defer_destroy(a);
175
            }
176
            assert!(!(*guard.local).bag.with(|b| (*b).is_empty()));
177
        }
178
    }
179
180
    #[test]
181
    fn pin_holds_advance() {
182
        #[cfg(miri)]
183
        const N: usize = 500;
184
        #[cfg(not(miri))]
185
        const N: usize = 500_000;
186
187
        let collector = Collector::new();
188
189
        thread::scope(|scope| {
190
            for _ in 0..NUM_THREADS {
191
                scope.spawn(|_| {
192
                    let handle = collector.register();
193
                    for _ in 0..N {
194
                        let guard = &handle.pin();
195
196
                        let before = collector.global.epoch.load(Ordering::Relaxed);
197
                        collector.global.collect(guard);
198
                        let after = collector.global.epoch.load(Ordering::Relaxed);
199
200
                        assert!(after.wrapping_sub(before) <= 2);
201
                    }
202
                });
203
            }
204
        })
205
        .unwrap();
206
    }
207
208
    #[cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to `cfg(crossbeam_sanitize)` reduce `internal::MAX_OBJECTS`
209
    #[test]
210
    fn incremental() {
211
        #[cfg(miri)]
212
        const COUNT: usize = 500;
213
        #[cfg(not(miri))]
214
        const COUNT: usize = 100_000;
215
        static DESTROYS: AtomicUsize = AtomicUsize::new(0);
216
217
        let collector = Collector::new();
218
        let handle = collector.register();
219
220
        unsafe {
221
            let guard = &handle.pin();
222
            for _ in 0..COUNT {
223
                let a = Owned::new(7i32).into_shared(guard);
224
                guard.defer_unchecked(move || {
225
                    drop(a.into_owned());
226
                    DESTROYS.fetch_add(1, Ordering::Relaxed);
227
                });
228
            }
229
            guard.flush();
230
        }
231
232
        let mut last = 0;
233
234
        while last < COUNT {
235
            let curr = DESTROYS.load(Ordering::Relaxed);
236
            assert!(curr - last <= 1024);
237
            last = curr;
238
239
            let guard = &handle.pin();
240
            collector.global.collect(guard);
241
        }
242
        assert!(DESTROYS.load(Ordering::Relaxed) == COUNT);
243
    }
244
245
    #[test]
246
    fn buffering() {
247
        const COUNT: usize = 10;
248
        #[cfg(miri)]
249
        const N: usize = 500;
250
        #[cfg(not(miri))]
251
        const N: usize = 100_000;
252
        static DESTROYS: AtomicUsize = AtomicUsize::new(0);
253
254
        let collector = Collector::new();
255
        let handle = collector.register();
256
257
        unsafe {
258
            let guard = &handle.pin();
259
            for _ in 0..COUNT {
260
                let a = Owned::new(7i32).into_shared(guard);
261
                guard.defer_unchecked(move || {
262
                    drop(a.into_owned());
263
                    DESTROYS.fetch_add(1, Ordering::Relaxed);
264
                });
265
            }
266
        }
267
268
        for _ in 0..N {
269
            collector.global.collect(&handle.pin());
270
        }
271
        assert!(DESTROYS.load(Ordering::Relaxed) < COUNT);
272
273
        handle.pin().flush();
274
275
        while DESTROYS.load(Ordering::Relaxed) < COUNT {
276
            let guard = &handle.pin();
277
            collector.global.collect(guard);
278
        }
279
        assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT);
280
    }
281
282
    #[test]
283
    fn count_drops() {
284
        #[cfg(miri)]
285
        const COUNT: usize = 500;
286
        #[cfg(not(miri))]
287
        const COUNT: usize = 100_000;
288
        static DROPS: AtomicUsize = AtomicUsize::new(0);
289
290
        struct Elem(#[allow(dead_code)] i32);
291
292
        impl Drop for Elem {
293
            fn drop(&mut self) {
294
                DROPS.fetch_add(1, Ordering::Relaxed);
295
            }
296
        }
297
298
        let collector = Collector::new();
299
        let handle = collector.register();
300
301
        unsafe {
302
            let guard = &handle.pin();
303
304
            for _ in 0..COUNT {
305
                let a = Owned::new(Elem(7i32)).into_shared(guard);
306
                guard.defer_destroy(a);
307
            }
308
            guard.flush();
309
        }
310
311
        while DROPS.load(Ordering::Relaxed) < COUNT {
312
            let guard = &handle.pin();
313
            collector.global.collect(guard);
314
        }
315
        assert_eq!(DROPS.load(Ordering::Relaxed), COUNT);
316
    }
317
318
    #[test]
319
    fn count_destroy() {
320
        #[cfg(miri)]
321
        const COUNT: usize = 500;
322
        #[cfg(not(miri))]
323
        const COUNT: usize = 100_000;
324
        static DESTROYS: AtomicUsize = AtomicUsize::new(0);
325
326
        let collector = Collector::new();
327
        let handle = collector.register();
328
329
        unsafe {
330
            let guard = &handle.pin();
331
332
            for _ in 0..COUNT {
333
                let a = Owned::new(7i32).into_shared(guard);
334
                guard.defer_unchecked(move || {
335
                    drop(a.into_owned());
336
                    DESTROYS.fetch_add(1, Ordering::Relaxed);
337
                });
338
            }
339
            guard.flush();
340
        }
341
342
        while DESTROYS.load(Ordering::Relaxed) < COUNT {
343
            let guard = &handle.pin();
344
            collector.global.collect(guard);
345
        }
346
        assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT);
347
    }
348
349
    #[test]
350
    fn drop_array() {
351
        const COUNT: usize = 700;
352
        static DROPS: AtomicUsize = AtomicUsize::new(0);
353
354
        struct Elem(#[allow(dead_code)] i32);
355
356
        impl Drop for Elem {
357
            fn drop(&mut self) {
358
                DROPS.fetch_add(1, Ordering::Relaxed);
359
            }
360
        }
361
362
        let collector = Collector::new();
363
        let handle = collector.register();
364
365
        let mut guard = handle.pin();
366
367
        let mut v = Vec::with_capacity(COUNT);
368
        for i in 0..COUNT {
369
            v.push(Elem(i as i32));
370
        }
371
372
        {
373
            let a = Owned::new(v).into_shared(&guard);
374
            unsafe {
375
                guard.defer_destroy(a);
376
            }
377
            guard.flush();
378
        }
379
380
        while DROPS.load(Ordering::Relaxed) < COUNT {
381
            guard.repin();
382
            collector.global.collect(&guard);
383
        }
384
        assert_eq!(DROPS.load(Ordering::Relaxed), COUNT);
385
    }
386
387
    #[test]
388
    fn destroy_array() {
389
        #[cfg(miri)]
390
        const COUNT: usize = 500;
391
        #[cfg(not(miri))]
392
        const COUNT: usize = 100_000;
393
        static DESTROYS: AtomicUsize = AtomicUsize::new(0);
394
395
        let collector = Collector::new();
396
        let handle = collector.register();
397
398
        unsafe {
399
            let guard = &handle.pin();
400
401
            let mut v = Vec::with_capacity(COUNT);
402
            for i in 0..COUNT {
403
                v.push(i as i32);
404
            }
405
406
            let len = v.len();
407
            let cap = v.capacity();
408
            let ptr = ManuallyDrop::new(v).as_mut_ptr();
409
            guard.defer_unchecked(move || {
410
                drop(Vec::from_raw_parts(ptr, len, cap));
411
                DESTROYS.fetch_add(len, Ordering::Relaxed);
412
            });
413
            guard.flush();
414
        }
415
416
        while DESTROYS.load(Ordering::Relaxed) < COUNT {
417
            let guard = &handle.pin();
418
            collector.global.collect(guard);
419
        }
420
        assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT);
421
    }
422
423
    #[test]
424
    fn stress() {
425
        const THREADS: usize = 8;
426
        #[cfg(miri)]
427
        const COUNT: usize = 500;
428
        #[cfg(not(miri))]
429
        const COUNT: usize = 100_000;
430
        static DROPS: AtomicUsize = AtomicUsize::new(0);
431
432
        struct Elem(#[allow(dead_code)] i32);
433
434
        impl Drop for Elem {
435
            fn drop(&mut self) {
436
                DROPS.fetch_add(1, Ordering::Relaxed);
437
            }
438
        }
439
440
        let collector = Collector::new();
441
442
        thread::scope(|scope| {
443
            for _ in 0..THREADS {
444
                scope.spawn(|_| {
445
                    let handle = collector.register();
446
                    for _ in 0..COUNT {
447
                        let guard = &handle.pin();
448
                        unsafe {
449
                            let a = Owned::new(Elem(7i32)).into_shared(guard);
450
                            guard.defer_destroy(a);
451
                        }
452
                    }
453
                });
454
            }
455
        })
456
        .unwrap();
457
458
        let handle = collector.register();
459
        while DROPS.load(Ordering::Relaxed) < COUNT * THREADS {
460
            let guard = &handle.pin();
461
            collector.global.collect(guard);
462
        }
463
        assert_eq!(DROPS.load(Ordering::Relaxed), COUNT * THREADS);
464
    }
465
}