Struct Collector

Source
pub struct Collector { /* private fields */ }
Expand description

A concurrent garbage collector.

A Collector manages the access and retirement of concurrent objects Objects can be safely loaded through guards, which can be created using the enter or enter_owned methods.

Every instance of a concurrent data structure should typically own its Collector. This allows the garbage collection of non-'static values, as memory reclamation is guaranteed to run when the Collector is dropped.

Implementations§

Source§

impl Collector

Source

pub fn new() -> Self

Creates a new collector.

Source

pub fn batch_size(self, batch_size: usize) -> Self

Sets the number of objects that must be in a batch before reclamation is attempted.

Retired objects are added to thread-local batches before starting the reclamation process. After batch_size is hit, the objects are moved to separate retirement lists, where reference counting kicks in and batches are eventually reclaimed.

A larger batch size amortizes the cost of retirement. However, reclamation latency can also grow due to the large number of objects needed to be freed. Note that reclamation can not be attempted unless the batch contains at least as many objects as the number of active threads.

The default batch size is 32.

Source

pub fn enter(&self) -> LocalGuard<'_>

Marks the current thread as active, returning a guard that protects loads of concurrent objects for its lifetime. The thread will be marked as inactive when the guard is dropped.

Note that loads of objects that may be retired must be protected with the Guard::protect. See the guide for an introduction to using guards, or the documentation of LocalGuard for more details.

Note that enter is reentrant, and it is legal to create multiple guards on the same thread. The thread will stay marked as active until the last guard is dropped.

§Performance

Performance-wise, creating and destroying a LocalGuard is about the same as locking and unlocking an uncontended Mutex. Because of this, guards should be reused across multiple operations if possible. However, holding a guard prevents the reclamation of any concurrent objects retired during its lifetime, so there is a tradeoff between performance and memory usage.

§Examples
use seize::Guard;
  
// An atomic object.
let ptr = AtomicPtr::new(Box::into_raw(Box::new(1_usize)));

{
    // Create a guard that is active for this scope.
    let guard = collector.enter();

    // Read the object using a protected load.
    let value = guard.protect(&ptr, Ordering::Acquire);
    unsafe { assert_eq!(*value, 1) }

    // If there are other thread that may retire the object,
    // the pointer is no longer valid after the guard is dropped.
    drop(guard);
}
Source

pub fn enter_owned(&self) -> OwnedGuard<'_>

Create an owned guard that protects objects for its lifetime.

Unlike local guards created with enter, owned guards are independent of the current thread, allowing them to implement Send and Sync. See the documentation of OwnedGuard for more details.

Source

pub unsafe fn retire<T>( &self, ptr: *mut T, reclaim: unsafe fn(*mut T, &Collector), )

Retires a value, running reclaim when no threads hold a reference to it.

Note that this method is disconnected from any guards on the current thread, so the pointer may be reclaimed immediately. Use Guard::defer_retire if the pointer may still be accessed by the current thread while the guard is active.

§Safety

The retired pointer must no longer be accessible to any thread that enters after it is removed. It also cannot be accessed by the current thread after retire is called.

Additionally, the pointer must be valid to pass to the provided reclaimer, once it is safe to reclaim.

§Examples

Common reclaimers are provided by the reclaim module.

use seize::reclaim;

// An atomic object.
let ptr = AtomicPtr::new(Box::into_raw(Box::new(1_usize)));

// Create a guard.
let guard = collector.enter();

// Store a new value.
let old = ptr.swap(Box::into_raw(Box::new(2_usize)), Ordering::Release);

// Reclaim the old value.
//
// Safety: The `swap` above made the old value unreachable for any new threads.
// Additionally, the old value was allocated with a `Box`, so `reclaim::boxed`
// is valid.
unsafe { collector.retire(old, reclaim::boxed) };

Alternative, a custom reclaimer function can be used.

use seize::Collector;

let collector = Collector::new();

// Allocate a value and immediately retire it.
let value: *mut usize = Box::into_raw(Box::new(1_usize));

// Safety: The value was never shared.
unsafe {
    collector.retire(value, |ptr: *mut usize, _collector: &Collector| unsafe {
        // Safety: The value was allocated with `Box::new`.
        let value = Box::from_raw(ptr);
        println!("Dropping {value}");
        drop(value);
    });
}
Source

pub unsafe fn reclaim_all(&self)

Reclaim any values that have been retired.

This method reclaims any objects that have been retired across all threads. After calling this method, any values that were previous retired, or retired recursively on the current thread during this call, will have been reclaimed.

§Safety

This function is extremely unsafe to call. It is only sound when no threads are currently active, whether accessing values that have been retired or accessing the collector through any type of guard. This is akin to having a unique reference to the collector. However, this method takes a shared reference, as reclaimers to be run by this thread are allowed to access the collector recursively.

§Notes

Note that if reclaimers initialize guards across threads, or initialize owned guards, objects retired through those guards may not be reclaimed.

Trait Implementations§

Source§

impl Debug for Collector

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Collector

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Collector

Source§

fn eq(&self, other: &Self) -> bool

Checks if both references point to the same collector.

1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for Collector

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.