seize/
collector.rs

1use crate::tls::Thread;
2use crate::{membarrier, raw, LocalGuard, OwnedGuard};
3
4use std::cell::UnsafeCell;
5use std::fmt;
6use std::num::NonZeroU64;
7use std::sync::atomic::{AtomicUsize, Ordering};
8
9/// Fast, efficient, and robust memory reclamation.
10///
11/// A `Collector` manages the allocation and retirement of concurrent objects.
12/// Objects can be safely loaded through *guards*, which can be created using
13/// the [`enter`](Collector::enter) or [`enter_owned`](Collector::enter_owned)
14/// methods.
15pub struct Collector {
16    id: usize,
17    pub(crate) raw: raw::Collector,
18}
19
20unsafe impl Send for Collector {}
21unsafe impl Sync for Collector {}
22
23impl Collector {
24    const DEFAULT_BATCH_SIZE: usize = 64;
25    const DEFAULT_EPOCH_TICK: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(110) };
26
27    /// Creates a new collector.
28    pub fn new() -> Self {
29        static ID: AtomicUsize = AtomicUsize::new(0);
30
31        membarrier::detect();
32        let cpus = std::thread::available_parallelism()
33            .map(Into::into)
34            .unwrap_or(1);
35
36        let batch_size = cpus.min(Self::DEFAULT_BATCH_SIZE);
37
38        Self {
39            raw: raw::Collector::new(cpus, Self::DEFAULT_EPOCH_TICK, batch_size),
40            id: ID.fetch_add(1, Ordering::Relaxed),
41        }
42    }
43
44    /// Sets the frequency of epoch advancement.
45    ///
46    /// Seize uses epochs to protect against stalled threads.
47    /// The more frequently the epoch is advanced, the faster
48    /// stalled threads can be detected. However, it also means
49    /// that threads will have to do work to catch up to the
50    /// current epoch more often.
51    ///
52    /// The default epoch frequency is `110`, meaning that
53    /// the epoch will advance after every 110 values are
54    /// linked to the collector. Benchmarking has shown that
55    /// this is a good tradeoff between throughput and memory
56    /// efficiency.
57    ///
58    /// If `None` is passed epoch tracking, and protection
59    /// against stalled threads, will be disabled completely.
60    pub fn epoch_frequency(mut self, n: Option<NonZeroU64>) -> Self {
61        self.raw.epoch_frequency = n;
62        self
63    }
64
65    /// Sets the number of values that must be in a batch
66    /// before reclamation is attempted.
67    ///
68    /// Retired values are added to thread-local *batches*
69    /// before starting the reclamation process. After
70    /// `batch_size` is hit, values are moved to separate
71    /// *retirement lists*, where reference counting kicks
72    /// in and batches are eventually reclaimed.
73    ///
74    /// A larger batch size means that deallocation is done
75    /// less frequently, but reclamation also becomes more
76    /// expensive due to longer retirement lists needing
77    /// to be traversed and freed.
78    ///
79    /// Note that batch sizes should generally be larger
80    /// than the number of threads accessing objects.
81    ///
82    /// The default batch size is `32`.
83    pub fn batch_size(mut self, batch_size: usize) -> Self {
84        self.raw.batch_size = batch_size;
85        self
86    }
87
88    /// Marks the current thread as active, returning a guard
89    /// that allows protecting loads of concurrent objects. The thread
90    /// will be marked as inactive when the guard is dropped.
91    ///
92    /// See [the guide](crate::guide#starting-operations) for an
93    /// introduction to using guards, or the documentation of [`LocalGuard`]
94    /// for more details.
95    ///
96    /// # Performance
97    ///
98    /// Creating and destroying a guard is about the same as locking and
99    /// unlocking an uncontended `Mutex`, performance-wise. Because of this,
100    /// guards should be re-used across multiple operations if possible.
101    /// However, note that holding a guard prevents the reclamation of any
102    /// concurrent objects retired during it's lifetime, so there is a
103    /// tradeoff between performance and memory usage.
104    ///
105    /// # Examples
106    ///
107    /// ```rust
108    /// # use std::sync::atomic::{AtomicPtr, Ordering};
109    /// # let collector = seize::Collector::new();
110    /// use seize::{reclaim, Linked, Guard};
111    ///
112    /// let ptr = AtomicPtr::new(collector.link_boxed(1_usize));
113    ///
114    /// let guard = collector.enter();
115    /// let value = guard.protect(&ptr, Ordering::Acquire);
116    /// unsafe { assert_eq!(**value, 1) }
117    /// # unsafe { guard.defer_retire(value, reclaim::boxed::<Linked<usize>>) };
118    /// ```
119    ///
120    /// Note that `enter` is reentrant, and it is legal to create
121    /// multiple guards on the same thread. The thread will stay
122    /// marked as active until the last guard is dropped:
123    ///
124    /// ```rust
125    /// # use std::sync::atomic::{AtomicPtr, Ordering};
126    /// # let collector = seize::Collector::new();
127    /// use seize::{reclaim, Linked, Guard};
128    ///
129    /// let ptr = AtomicPtr::new(collector.link_boxed(1_usize));
130    ///
131    /// let guard1 = collector.enter();
132    /// let guard2 = collector.enter();
133    ///
134    /// let value = guard2.protect(&ptr, Ordering::Acquire);
135    /// drop(guard1);
136    /// // the first guard is dropped, but `value`
137    /// // is still safe to access as a guard still
138    /// // exists
139    /// unsafe { assert_eq!(**value, 1) }
140    /// # unsafe { guard2.defer_retire(value, reclaim::boxed::<Linked<usize>>) };
141    /// drop(guard2) // _now_, the thread is marked as inactive
142    /// ```
143    #[inline]
144    pub fn enter(&self) -> LocalGuard<'_> {
145        LocalGuard::enter(self)
146    }
147
148    /// Create an owned guard that protects objects for it's lifetime.
149    ///
150    /// Unlike local guards created with [`enter`](Collector::enter),
151    /// owned guards are independent of the current thread, allowing
152    /// them to implement `Send`. See the documentation of [`OwnedGuard`]
153    /// for more details.
154    #[inline]
155    pub fn enter_owned(&self) -> OwnedGuard<'_> {
156        OwnedGuard::enter(self)
157    }
158
159    /// Create a [`Link`] that can be used to link an object to the collector.
160    ///
161    /// This method is useful when working with a DST where the [`Linked`]
162    /// wrapper cannot be used. See [`AsLink`] for details, or use the
163    /// [`link_value`](Collector::link_value)
164    /// and [`link_boxed`](Collector::link_boxed) helpers.
165    #[inline]
166    pub fn link(&self) -> Link {
167        // Safety: Accessing the reservation of the current thread is always valid.
168        let reservation = unsafe { self.raw.reservation(Thread::current()) };
169
170        Link {
171            node: UnsafeCell::new(self.raw.node(reservation)),
172        }
173    }
174
175    /// Creates a new `Linked` object with the given value.
176    ///
177    /// This is equivalent to:
178    ///
179    /// ```ignore
180    /// Linked {
181    ///     value,
182    ///     link: collector.link()
183    /// }
184    /// ```
185    #[inline]
186    pub fn link_value<T>(&self, value: T) -> Linked<T> {
187        Linked {
188            link: self.link(),
189            value,
190        }
191    }
192
193    /// Links a value to the collector and allocates it with `Box`.
194    ///
195    /// This is equivalent to:
196    ///
197    /// ```ignore
198    /// Box::into_raw(Box::new(Linked {
199    ///     value,
200    ///     link: collector.link()
201    /// }))
202    /// ```
203    #[inline]
204    pub fn link_boxed<T>(&self, value: T) -> *mut Linked<T> {
205        Box::into_raw(Box::new(Linked {
206            link: self.link(),
207            value,
208        }))
209    }
210
211    /// Retires a value, running `reclaim` when no threads hold a reference to
212    /// it.
213    ///
214    /// Note that this method is disconnected from any guards on the current
215    /// thread, so the pointer may be reclaimed immediately. Use
216    /// [`Guard::defer_retire`](crate::Guard::defer_retire) if the pointer
217    /// may still be accessed by the current thread.
218    ///
219    /// # Safety
220    ///
221    /// The retired object must no longer be accessible to any thread that
222    /// enters after it is removed. It also cannot be accessed by the
223    /// current thread after `retire` is called.
224    ///
225    /// Retiring the same pointer twice can cause **undefined behavior**, even
226    /// if the reclaimer doesn't free memory.
227    ///
228    /// Additionally, the pointer must be valid to access as a [`Link`], per the
229    /// [`AsLink`] trait, and the reclaimer passed to `retire` must
230    /// correctly free values of type `T`.
231    ///
232    /// # Examples
233    ///
234    /// Common reclaimers are provided by the [`reclaim`](crate::reclaim)
235    /// module.
236    ///
237    /// ```
238    /// # use std::sync::atomic::{AtomicPtr, Ordering};
239    /// # let collector = seize::Collector::new();
240    /// use seize::{reclaim, Linked};
241    ///
242    /// let ptr = AtomicPtr::new(collector.link_boxed(1_usize));
243    ///
244    /// let guard = collector.enter();
245    /// // store the new value
246    /// let old = ptr.swap(collector.link_boxed(2_usize), Ordering::Release);
247    /// // reclaim the old value
248    /// // safety: the `swap` above made the old value unreachable for any new threads
249    /// unsafe { collector.retire(old, reclaim::boxed::<Linked<usize>>) };
250    /// # unsafe { collector.retire(ptr.load(Ordering::Relaxed), reclaim::boxed::<Linked<usize>>) };
251    /// ```
252    ///
253    /// Alternative, a custom reclaimer function can be used:
254    ///
255    /// ```
256    /// # use seize::{Link, Collector, Linked};
257    /// # let collector = Collector::new();
258    /// let value = collector.link_boxed(1);
259    ///
260    /// // safety: the value was never shared
261    /// unsafe {
262    ///     collector.retire(value, |link: *mut Link| unsafe {
263    ///         // safety: the value retired was of type *mut Linked<i32>
264    ///         let ptr: *mut Linked<i32> = Link::cast(link);
265    ///
266    ///         // safety: the value was allocated with `link_boxed`
267    ///         let value = Box::from_raw(ptr);
268    ///         println!("dropping {}", value);
269    ///         drop(value);
270    ///     });
271    /// }
272    /// ```
273    #[inline]
274    pub unsafe fn retire<T: AsLink>(&self, ptr: *mut T, reclaim: unsafe fn(*mut Link)) {
275        debug_assert!(!ptr.is_null(), "attempted to retire null pointer");
276
277        // Note that `add` doesn't ever actually reclaim the pointer immediately if
278        // the current thread is active. Instead, it adds it to the current thread's
279        // reclamation list, but we don't guarantee that publicly.
280        unsafe { self.raw.add(ptr, reclaim, Thread::current()) }
281    }
282
283    /// Reclaim any values that have been retired.
284    ///
285    /// This method reclaims any objects that have been retired across *all*
286    /// threads. After calling this method, any values that were previous
287    /// retired, or retired recursively on the current thread during this
288    /// call, will have been reclaimed.
289    ///
290    /// # Safety
291    ///
292    /// This function is **extremely unsafe** to call. It is only sound when no
293    /// threads are currently active, whether accessing values that have
294    /// been retired or accessing the collector through any type of guard.
295    /// This is akin to having a unique reference to the collector. However,
296    /// this method takes a shared reference, as reclaimers to be run by this
297    /// thread are allowed to access the collector recursively.
298    ///
299    /// # Notes
300    ///
301    /// Note that if reclaimers initialize guards across threads, or initialize
302    /// owned guards, objects retired through those guards may not be
303    /// reclaimed.
304    pub unsafe fn reclaim_all(&self) {
305        unsafe { self.raw.reclaim_all() };
306    }
307
308    #[inline]
309    pub(crate) fn id_eq(this: &Collector, other: &Collector) -> bool {
310        this.id == other.id
311    }
312}
313
314impl Clone for Collector {
315    /// Creates a new, independent collector with the same configuration as this
316    /// one.
317    fn clone(&self) -> Self {
318        Collector::new()
319            .batch_size(self.raw.batch_size)
320            .epoch_frequency(self.raw.epoch_frequency)
321    }
322}
323
324impl Default for Collector {
325    fn default() -> Self {
326        Self::new()
327    }
328}
329
330impl fmt::Debug for Collector {
331    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332        let mut f = f.debug_struct("Collector");
333
334        if self.raw.epoch_frequency.is_some() {
335            f.field("epoch", &self.raw.epoch.load(Ordering::Acquire));
336        }
337
338        f.field("batch_size", &self.raw.batch_size)
339            .field("epoch_frequency", &self.raw.epoch_frequency)
340            .finish()
341    }
342}
343
344/// A link to the collector.
345///
346/// Functions passed to [`retire`](Collector::retire) are called with a
347/// type-erased `Link` pointer. To extract the underlying value from a link,
348/// you can call the [`cast`](Link::cast) method.
349#[repr(transparent)]
350pub struct Link {
351    #[allow(dead_code)]
352    pub(crate) node: UnsafeCell<raw::Node>,
353}
354
355impl Link {
356    /// Cast this `link` to it's underlying type.
357    ///
358    /// Note that while this function is safe, using the returned
359    /// pointer is only sound if the link is in fact a type-erased `T`.
360    /// This means that when casting a link in a reclaimer, the value
361    /// that was retired must be of type `T`.
362    #[inline]
363    pub fn cast<T: AsLink>(link: *mut Link) -> *mut T {
364        link.cast()
365    }
366}
367
368/// A type that can be pointer-cast to and from a [`Link`].
369///
370/// Most types can avoid this trait and work instead with the [`Linked`]
371/// wrapper type. However, if you want more control over the layout of your
372/// type (i.e. are working with a DST), you may need to implement this trait
373/// directly.
374///
375/// # Safety
376///
377/// Types implementing this trait must be marked `#[repr(C)]`
378/// and have a [`Link`] as their **first** field.
379///
380/// # Examples
381///
382/// ```rust
383/// use seize::{AsLink, Collector, Link};
384///
385/// #[repr(C)]
386/// struct Bytes {
387///     // safety invariant: Link must be the first field
388///     link: Link,
389///     values: [*mut u8; 0],
390/// }
391///
392/// // Safety: Bytes is repr(C) and has Link as it's first field
393/// unsafe impl AsLink for Bytes {}
394///
395/// // Deallocate an `Bytes`.
396/// unsafe fn dealloc(ptr: *mut Bytes, collector: &Collector) {
397///     collector.retire(ptr, |link| {
398///         // safety `ptr` is of type *mut Bytes
399///         let link: *mut Bytes = Link::cast(link);
400///         // ..
401///     });
402/// }
403/// ```
404pub unsafe trait AsLink {}
405
406/// A value linked to a collector.
407///
408/// Objects that may be retired must embed the [`Link`] type. This is a
409/// convenience wrapper for linked values that can be created with the
410/// [`Collector::link_value`] or [`Collector::link_boxed`] methods.
411#[repr(C)]
412pub struct Linked<T> {
413    pub link: Link, // Safety Invariant: this field must come first
414    pub value: T,
415}
416
417unsafe impl<T> AsLink for Linked<T> {}
418
419impl<T: PartialEq> PartialEq for Linked<T> {
420    #[inline]
421    fn eq(&self, other: &Self) -> bool {
422        self.value == other.value
423    }
424}
425
426impl<T: Eq> Eq for Linked<T> {}
427
428impl<T: fmt::Debug> fmt::Debug for Linked<T> {
429    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430        write!(f, "{:?}", self.value)
431    }
432}
433
434impl<T: fmt::Display> fmt::Display for Linked<T> {
435    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436        write!(f, "{}", self.value)
437    }
438}
439
440impl<T> std::ops::Deref for Linked<T> {
441    type Target = T;
442
443    #[inline]
444    fn deref(&self) -> &Self::Target {
445        &self.value
446    }
447}
448
449impl<T> std::ops::DerefMut for Linked<T> {
450    #[inline]
451    fn deref_mut(&mut self) -> &mut Self::Target {
452        &mut self.value
453    }
454}