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
impl Collector
Sourcepub fn epoch_frequency(self, n: Option<NonZeroU64>) -> Self
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.
Sourcepub fn batch_size(self, batch_size: usize) -> Self
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
.
Sourcepub fn enter(&self) -> LocalGuard<'_>
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
Sourcepub fn enter_owned(&self) -> OwnedGuard<'_>
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.
Sourcepub fn link(&self) -> Link
pub fn link(&self) -> Link
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.
Sourcepub fn link_value<T>(&self, value: T) -> Linked<T>
pub fn link_value<T>(&self, value: T) -> Linked<T>
Creates a new Linked
object with the given value.
This is equivalent to:
Linked {
value,
link: collector.link()
}
Sourcepub fn link_boxed<T>(&self, value: T) -> *mut Linked<T>
pub fn link_boxed<T>(&self, value: T) -> *mut Linked<T>
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()
}))
Sourcepub unsafe fn retire<T: AsLink>(
&self,
ptr: *mut T,
reclaim: unsafe fn(*mut Link),
)
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);
});
}
Sourcepub unsafe fn reclaim_all(&self)
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.