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, n: usize) -> Self
pub fn batch_size(self, n: 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 120. Tests have shown that
this makes a good tradeoff between throughput and memory
efficiency.
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 inactivesourcepub 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.
Additionally, 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);
});
}