Skip to main content

arctic/concurrent/
smr.rs

1//! Implementations of [`Smr`].
2//!
3//! By default, [`ConcurrentMap`][crate::concurrent::Map] uses [seize](https://docs.rs/seize/0.5.1/seize/)
4//! for safe memory reclamation. Support for
5//! [crossbeam-epoch](https://docs.rs/crossbeam-epoch/0.9.18/crossbeam_epoch/)
6//! and hazard keys can be enabled with the
7//! `smr-seize` and `smr-hazard` Cargo features, respectively.
8//! Downstream users can also implement [`Smr`] and [`Guard`]
9//! to provide their own SMR backends.
10
11#[cfg(feature = "smr-epoch")]
12mod epoch;
13/// Auxiliary types for use with hazard keys.
14#[cfg(feature = "smr-hazard")]
15pub mod hazard;
16/// Auxiliary types for use with no-op SMR.
17pub mod no_op;
18#[cfg(feature = "smr-seize")]
19mod seize;
20
21use core::num::NonZeroU64;
22
23#[cfg(feature = "smr-epoch")]
24pub use epoch::Epoch;
25#[doc(inline)]
26#[cfg(feature = "smr-hazard")]
27pub use hazard::Hazard;
28pub use no_op::NoOp;
29#[cfg(feature = "smr-seize")]
30pub use seize::Seize;
31
32cfg_select! {
33    feature = "smr-seize" => {
34        /// Default [`Smr`] backend.
35        pub type Default = Seize;
36    }
37    _ => {
38        /// Default [`Smr`] backend.
39        pub type Default = NoOp;
40    }
41}
42
43use crate::Key;
44use crate::concurrent::Value;
45use crate::stat;
46
47/// Provides [safe memory reclamation](https://arxiv.org/abs/2509.02457) for the
48/// given key and value type.
49pub trait Smr<K: Key, V: Value> {
50    /// Guard type that protects nodes and values during its lifetime,
51    /// can be used to retire allocations, and unprotects when it is dropped.
52    type Guard<'g>: Guard<V>
53    where
54        V: 'g,
55        Self: 'g;
56
57    /// Construct a [`Guard`] that protects nodes and values associated with `prefix`.
58    fn guard<'g>(&'g self, prefix: K::Read<'_>) -> Self::Guard<'g>
59    where
60        V: 'g;
61
62    /// Estimate the peak number of unreclaimed allocations.
63    fn garbage(&self) -> u32 {
64        0
65    }
66}
67
68/// Protects allocations from deallocation, and allows allocations to be retired.
69///
70/// External implementations may call [`deallocate_node`] and [`deallocate_value`]
71/// to deallocate nodes and values
72/// (passed via [`Guard::retire_node`] and [`Guard::retire_value`]) when they
73/// can determine there are no live references.
74pub trait Guard<V: Value> {
75    /// Retire an internal node with prefix length `bits`.
76    ///
77    /// # Safety
78    ///
79    /// Caller must guarantee `node` is a valid node pointer.
80    unsafe fn retire_node(&mut self, bits: usize, node: NonZeroU64);
81
82    /// Retire a value.
83    ///
84    /// # Safety
85    ///
86    /// Caller must guarantee `value` is a valid value pointer.
87    unsafe fn retire_value(&mut self, value: u64);
88}
89
90/// Deallocate a previously retired node.
91///
92/// # Safety
93///
94/// Caller must guarantee there are no live references to `node`,
95/// and that `node` was previously retired via [`Guard::retire_node`].
96pub unsafe fn deallocate_node(node: NonZeroU64) {
97    stat::increment(stat::Counter::FreeRetire);
98    unsafe { ribbit::Packed::<crate::raw::node::Ptr>::from_raw_unchecked(node).deallocate() }
99}
100
101/// Deallocate a previously retired value.
102///
103/// # Safety
104///
105/// Caller must guarantee there are no live references to `value`,
106/// and that `value` was previously retired via [`Guard::<V>::retire_value`][`Guard::retire_value`].
107pub unsafe fn deallocate_value<V: Value>(value: u64) {
108    stat::increment(stat::Counter::FreeRetire);
109    drop(unsafe { V::from_raw_unchecked(value) })
110}