Struct Collector

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

Fast, efficient, and robust memory reclamation.

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

Implementations§

Source§

impl Collector

Source

pub fn new() -> Self

Creates a new collector.

Source

pub fn epoch_frequency(self, n: Option<NonZeroU64>) -> Self

Sets the frequency of epoch advancement.

Seize uses epochs to protect against stalled threads. The more frequently the epoch is advanced, the faster stalled threads can be detected. However, it also means that threads will have to do work to catch up to the current epoch more often.

The default epoch frequency is 110, meaning that the epoch will advance after every 110 values are linked to the collector. Benchmarking has shown that this is a good tradeoff between throughput and memory efficiency.

If None is passed epoch tracking, and protection against stalled threads, will be disabled completely.

Source

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

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

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

A larger batch size means that deallocation is done less frequently, but reclamation also becomes more expensive due to longer retirement lists needing to be traversed and freed.

Note that batch sizes should generally be larger than the number of threads accessing objects.

The default batch size is 32.

Source

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

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

See the guide for an introduction to using guards, or the documentation of LocalGuard for more details.

§Performance

Creating and destroying a guard is about the same as locking and unlocking an uncontended Mutex, performance-wise. Because of this, guards should be re-used across multiple operations if possible. However, note that holding a guard prevents the reclamation of any concurrent objects retired during it’s lifetime, so there is a tradeoff between performance and memory usage.

§Examples
use seize::{reclaim, Linked, Guard};

let ptr = AtomicPtr::new(collector.link_boxed(1_usize));

let guard = collector.enter();
let value = guard.protect(&ptr, Ordering::Acquire);
unsafe { assert_eq!(**value, 1) }

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:

use seize::{reclaim, Linked, Guard};

let ptr = AtomicPtr::new(collector.link_boxed(1_usize));

let guard1 = collector.enter();
let guard2 = collector.enter();

let value = guard2.protect(&ptr, Ordering::Acquire);
drop(guard1);
// the first guard is dropped, but `value`
// is still safe to access as a guard still
// exists
unsafe { assert_eq!(**value, 1) }
drop(guard2) // _now_, the thread is marked as inactive
Source

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

Create an owned guard that protects objects for it’s lifetime.

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

Create a Link that can be used to link an object to the collector.

This method is useful when working with a DST where the Linked wrapper cannot be used. See AsLink for details, or use the link_value and link_boxed helpers.

Creates a new Linked object with the given value.

This is equivalent to:

Linked {
    value,
    link: collector.link()
}

Links a value to the collector and allocates it with Box.

This is equivalent to:

Box::into_raw(Box::new(Linked {
    value,
    link: collector.link()
}))
Source

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

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.

§Safety

The retired object 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.

Retiring the same pointer twice can cause undefined behavior, even if the reclaimer doesn’t free memory.

Additionally, the pointer must be valid to access as a Link, per the AsLink trait, and the reclaimer passed to retire must correctly free values of type T.

§Examples

Common reclaimers are provided by the reclaim module.

use seize::{reclaim, Linked};

let ptr = AtomicPtr::new(collector.link_boxed(1_usize));

let guard = collector.enter();
// store the new value
let old = ptr.swap(collector.link_boxed(2_usize), Ordering::Release);
// reclaim the old value
// safety: the `swap` above made the old value unreachable for any new threads
unsafe { collector.retire(old, reclaim::boxed::<Linked<usize>>) };

Alternative, a custom reclaimer function can be used:

let value = collector.link_boxed(1);

// safety: the value was never shared
unsafe {
    collector.retire(value, |link: *mut Link| unsafe {
        // safety: the value retired was of type *mut Linked<i32>
        let ptr: *mut Linked<i32> = Link::cast(link);

        // safety: the value was allocated with `link_boxed`
        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 Clone for Collector

Source§

fn clone(&self) -> Self

Creates a new, independent collector with the same configuration as this one.

1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
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 Send for Collector

Source§

impl Sync 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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dst: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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.