/// Epoch-based garbage collector.
///
/// # Examples
///
/// ```
/// use crossbeam_epoch::Collector;
///
/// let collector = Collector::new();
///
/// let handle = collector.register();
/// drop(collector); // `handle` still works after dropping `collector`
///
/// handle.pin().flush();
/// ```
use core::fmt;
use crate ::guard::Guard;
use crate ::internal::{Global, Local};
use crate ::primitive::sync::Arc;
/// An epoch-based garbage collector.
pub struct Collector {
pub (crate ) global: Arc<Global>,
}
unsafe impl Send for Collector {}
unsafe impl Sync for Collector {}
impl Default for Collector {
fn default() -> Self {
Self {
global: Arc::new(Global::new()),
}
}
}
impl Collector {
/// Creates a new collector.
pub fn new() -> Self {
Self ::default()
}
/// Registers a new handle for the collector.
pub fn register(&self ) -> LocalHandle {
Local::register(self )
}
}
impl Clone for Collector {
/// Creates another reference to the same garbage collector.
fn clone(&self ) -> Self {
Collector {
global: self .global.clone(),
}
}
}
impl fmt::Debug for Collector {
fn fmt(&self , f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("Collector { .. }" )
}
}
impl PartialEq for Collector {
/// Checks if both handles point to the same collector.
fn eq(&self , rhs: &Collector) -> bool {
Arc::ptr_eq(&self .global, &rhs.global)
}
}
impl Eq for Collector {}
/// A handle to a garbage collector.
pub struct LocalHandle {
pub (crate ) local: *const Local,
}
impl LocalHandle {
/// Pins the handle.
#[ inline]
pub fn pin(&self ) -> Guard {
unsafe { (*self .local).pin() }
}
/// Returns `true` if the handle is pinned.
#[ inline]
pub fn is_pinned(&self ) -> bool {
unsafe { (*self .local).is_pinned() }
}
/// Returns the `Collector` associated with this handle.
#[ inline]
pub fn collector(&self ) -> &Collector {
unsafe { (*self .local).collector() }
}
}
impl Drop for LocalHandle {
#[ inline]
fn drop(&mut self ) {
unsafe {
Local::release_handle(&*self .local);
}
}
}
impl fmt::Debug for LocalHandle {
fn fmt(&self , f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("LocalHandle { .. }" )
}
}
#[ cfg(all(test, not(crossbeam_loom)))]
mod tests {
use std::mem::ManuallyDrop;
use std::sync::atomic::{AtomicUsize, Ordering};
use crossbeam_utils::thread;
use crate ::{Collector, Owned};
const NUM_THREADS: usize = 8 ;
#[ test]
fn pin_reentrant() {
let collector = Collector::new();
let handle = collector.register();
drop(collector);
assert!(!handle.is_pinned());
{
let _guard = &handle.pin();
assert!(handle.is_pinned());
{
let _guard = &handle.pin();
assert!(handle.is_pinned());
}
assert!(handle.is_pinned());
}
assert!(!handle.is_pinned());
}
#[ test]
fn flush_local_bag() {
let collector = Collector::new();
let handle = collector.register();
drop(collector);
for _ in 0 ..100 {
let guard = &handle.pin();
unsafe {
let a = Owned::new(7 ).into_shared(guard);
guard.defer_destroy(a);
assert!(!(*guard.local).bag.with(|b| (*b).is_empty()));
while !(*guard.local).bag.with(|b| (*b).is_empty()) {
guard.flush();
}
}
}
}
#[ test]
fn garbage_buffering() {
let collector = Collector::new();
let handle = collector.register();
drop(collector);
let guard = &handle.pin();
unsafe {
for _ in 0 ..10 {
let a = Owned::new(7 ).into_shared(guard);
guard.defer_destroy(a);
}
assert!(!(*guard.local).bag.with(|b| (*b).is_empty()));
}
}
#[ test]
fn pin_holds_advance() {
#[ cfg(miri)]
const N: usize = 500 ;
#[ cfg(not(miri))]
const N: usize = 500 _000 ;
let collector = Collector::new();
thread::scope(|scope| {
for _ in 0 ..NUM_THREADS {
scope.spawn(|_| {
let handle = collector.register();
for _ in 0 ..N {
let guard = &handle.pin();
let before = collector.global.epoch.load(Ordering::Relaxed);
collector.global.collect(guard);
let after = collector.global.epoch.load(Ordering::Relaxed);
assert!(after.wrapping_sub(before) <= 2 );
}
});
}
})
.unwrap();
}
#[ cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to `cfg(crossbeam_sanitize)` reduce `internal::MAX_OBJECTS`
#[ test]
fn incremental() {
#[ cfg(miri)]
const COUNT: usize = 500 ;
#[ cfg(not(miri))]
const COUNT: usize = 100 _000 ;
static DESTROYS: AtomicUsize = AtomicUsize::new(0 );
let collector = Collector::new();
let handle = collector.register();
unsafe {
let guard = &handle.pin();
for _ in 0 ..COUNT {
let a = Owned::new(7 i32).into_shared(guard);
guard.defer_unchecked(move || {
drop(a.into_owned());
DESTROYS.fetch_add(1 , Ordering::Relaxed);
});
}
guard.flush();
}
let mut last = 0 ;
while last < COUNT {
let curr = DESTROYS.load(Ordering::Relaxed);
assert!(curr - last <= 1024 );
last = curr;
let guard = &handle.pin();
collector.global.collect(guard);
}
assert!(DESTROYS.load(Ordering::Relaxed) == COUNT);
}
#[ test]
fn buffering() {
const COUNT: usize = 10 ;
#[ cfg(miri)]
const N: usize = 500 ;
#[ cfg(not(miri))]
const N: usize = 100 _000 ;
static DESTROYS: AtomicUsize = AtomicUsize::new(0 );
let collector = Collector::new();
let handle = collector.register();
unsafe {
let guard = &handle.pin();
for _ in 0 ..COUNT {
let a = Owned::new(7 i32).into_shared(guard);
guard.defer_unchecked(move || {
drop(a.into_owned());
DESTROYS.fetch_add(1 , Ordering::Relaxed);
});
}
}
for _ in 0 ..N {
collector.global.collect(&handle.pin());
}
assert!(DESTROYS.load(Ordering::Relaxed) < COUNT);
handle.pin().flush();
while DESTROYS.load(Ordering::Relaxed) < COUNT {
let guard = &handle.pin();
collector.global.collect(guard);
}
assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT);
}
#[ test]
fn count_drops() {
#[ cfg(miri)]
const COUNT: usize = 500 ;
#[ cfg(not(miri))]
const COUNT: usize = 100 _000 ;
static DROPS: AtomicUsize = AtomicUsize::new(0 );
struct Elem(i32);
impl Drop for Elem {
fn drop(&mut self ) {
DROPS.fetch_add(1 , Ordering::Relaxed);
}
}
let collector = Collector::new();
let handle = collector.register();
unsafe {
let guard = &handle.pin();
for _ in 0 ..COUNT {
let a = Owned::new(Elem(7 i32)).into_shared(guard);
guard.defer_destroy(a);
}
guard.flush();
}
while DROPS.load(Ordering::Relaxed) < COUNT {
let guard = &handle.pin();
collector.global.collect(guard);
}
assert_eq!(DROPS.load(Ordering::Relaxed), COUNT);
}
#[ test]
fn count_destroy() {
#[ cfg(miri)]
const COUNT: usize = 500 ;
#[ cfg(not(miri))]
const COUNT: usize = 100 _000 ;
static DESTROYS: AtomicUsize = AtomicUsize::new(0 );
let collector = Collector::new();
let handle = collector.register();
unsafe {
let guard = &handle.pin();
for _ in 0 ..COUNT {
let a = Owned::new(7 i32).into_shared(guard);
guard.defer_unchecked(move || {
drop(a.into_owned());
DESTROYS.fetch_add(1 , Ordering::Relaxed);
});
}
guard.flush();
}
while DESTROYS.load(Ordering::Relaxed) < COUNT {
let guard = &handle.pin();
collector.global.collect(guard);
}
assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT);
}
#[ test]
fn drop_array() {
const COUNT: usize = 700 ;
static DROPS: AtomicUsize = AtomicUsize::new(0 );
struct Elem(i32);
impl Drop for Elem {
fn drop(&mut self ) {
DROPS.fetch_add(1 , Ordering::Relaxed);
}
}
let collector = Collector::new();
let handle = collector.register();
let mut guard = handle.pin();
let mut v = Vec::with_capacity(COUNT);
for i in 0 ..COUNT {
v.push(Elem(i as i32));
}
{
let a = Owned::new(v).into_shared(&guard);
unsafe {
guard.defer_destroy(a);
}
guard.flush();
}
while DROPS.load(Ordering::Relaxed) < COUNT {
guard.repin();
collector.global.collect(&guard);
}
assert_eq!(DROPS.load(Ordering::Relaxed), COUNT);
}
#[ test]
fn destroy_array() {
#[ cfg(miri)]
const COUNT: usize = 500 ;
#[ cfg(not(miri))]
const COUNT: usize = 100 _000 ;
static DESTROYS: AtomicUsize = AtomicUsize::new(0 );
let collector = Collector::new();
let handle = collector.register();
unsafe {
let guard = &handle.pin();
let mut v = Vec::with_capacity(COUNT);
for i in 0 ..COUNT {
v.push(i as i32);
}
let len = v.len();
let ptr = ManuallyDrop::new(v).as_mut_ptr() as usize;
guard.defer_unchecked(move || {
drop(Vec::from_raw_parts(ptr as *const i32 as *mut i32, len, len));
DESTROYS.fetch_add(len, Ordering::Relaxed);
});
guard.flush();
}
while DESTROYS.load(Ordering::Relaxed) < COUNT {
let guard = &handle.pin();
collector.global.collect(guard);
}
assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT);
}
#[ test]
fn stress() {
const THREADS: usize = 8 ;
#[ cfg(miri)]
const COUNT: usize = 500 ;
#[ cfg(not(miri))]
const COUNT: usize = 100 _000 ;
static DROPS: AtomicUsize = AtomicUsize::new(0 );
struct Elem(i32);
impl Drop for Elem {
fn drop(&mut self ) {
DROPS.fetch_add(1 , Ordering::Relaxed);
}
}
let collector = Collector::new();
thread::scope(|scope| {
for _ in 0 ..THREADS {
scope.spawn(|_| {
let handle = collector.register();
for _ in 0 ..COUNT {
let guard = &handle.pin();
unsafe {
let a = Owned::new(Elem(7 i32)).into_shared(guard);
guard.defer_destroy(a);
}
}
});
}
})
.unwrap();
let handle = collector.register();
while DROPS.load(Ordering::Relaxed) < COUNT * THREADS {
let guard = &handle.pin();
collector.global.collect(guard);
}
assert_eq!(DROPS.load(Ordering::Relaxed), COUNT * THREADS);
}
}
Messung V0.5 in Prozent C=81 H=96 G=88
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-27)
¤
*© Formatika GbR, Deutschland