Skip to main content

arctic/concurrent/smr/
seize.rs

1use core::num::NonZeroU64;
2
3use crate::Key;
4use crate::concurrent::Smr;
5use crate::concurrent::Value;
6use crate::concurrent::smr;
7use crate::stat;
8
9use seize::Guard as _;
10
11/// [`seize::Collector`] backend for safe memory reclamation.
12///
13/// Defaults to a batch size of 256, which we found to provide
14/// the best balance of throughput and reclamation efficiency
15/// in our benchmarks.
16///
17/// # Examples
18///
19/// ```rust
20/// use arctic::ConcurrentMap;
21/// use arctic::concurrent::smr::Seize;
22///
23/// let map = ConcurrentMap::<u64, Box<u64>, Seize>::with_smr(Seize::from(
24///     seize::Collector::new().batch_size(256)
25/// ));
26/// ```
27pub struct Seize(seize::Collector);
28
29impl Default for Seize {
30    fn default() -> Self {
31        Self(seize::Collector::default().batch_size(256))
32    }
33}
34
35impl From<seize::Collector> for Seize {
36    fn from(collector: seize::Collector) -> Self {
37        Self(collector)
38    }
39}
40
41impl From<Seize> for seize::Collector {
42    fn from(Seize(collector): Seize) -> Self {
43        collector
44    }
45}
46
47impl<K: Key, V: Value> Smr<K, V> for Seize {
48    type Guard<'g>
49        = seize::LocalGuard<'g>
50    where
51        V: 'g,
52        Self: 'g;
53
54    // NOTE: seize documentation says to call `seize::Guard::protect`
55    // on every pointer load, which loads with `SeqCst` ordering
56    // under the hood, but it's not clear to me why this is necessary?
57    //
58    // At least in arctic, pointers are always installed via a CAS
59    // with `AcqRel` ordering, so we should always see the correct
60    // contents of the allocation with `Acquire` loads.
61    fn guard<'g>(&'g self, _: K::Read<'_>) -> Self::Guard<'g>
62    where
63        V: 'g,
64    {
65        self.0.enter()
66    }
67}
68
69impl<'g, V: Value> smr::Guard<V> for seize::LocalGuard<'g> {
70    unsafe fn retire_node(&mut self, _bits: usize, node: NonZeroU64) {
71        stat::increment(stat::Counter::Retire);
72
73        unsafe {
74            self.defer_retire(node.get() as *mut (), |ptr, _| {
75                let node = NonZeroU64::new(ptr as u64).unwrap();
76                smr::deallocate_node(node);
77            })
78        }
79    }
80
81    unsafe fn retire_value(&mut self, value: u64) {
82        stat::increment(stat::Counter::Retire);
83
84        // HACK: Unfortunately, Seize does not natively support `defer_unchecked`.
85        // However, `defer_retire` does take an arbitrary closure to run at retire-time,
86        // and passes the `ptr` argument directly to it...
87        //
88        // See: [`seize::raw::Collector::add`] and [`seize::raw::Collector::try_retire`].
89        unsafe {
90            self.defer_retire(value as *mut (), |ptr, _| {
91                smr::deallocate_value::<V>(ptr as u64)
92            });
93        }
94    }
95}