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}