urcu/collections/hashmap/container.rs
1use std::hash::Hash;
2use std::ptr::NonNull;
3use std::sync::Arc;
4
5use anyhow::Result;
6
7use crate::collections::hashmap::iterator::Iter;
8use crate::collections::hashmap::raw::RawMap;
9use crate::collections::hashmap::reference::Ref;
10use crate::rcu::default::RcuDefaultFlavor;
11use crate::rcu::flavor::RcuFlavor;
12use crate::{RcuGuard, RcuReadContext, RcuRef};
13
14/// Defines a RCU lock-free hashmap.
15///
16/// This hashmap supports multiple concurrents readers and writers. It is guaranteed
17/// to never block on a call.
18///
19/// # Limitations
20///
21/// ##### Mutable References
22///
23/// Because there might always be readers borrowing a node's data, it is impossible
24/// to get a mutable references to the data inside the stack. You should design the
25/// type stored in the stack with [interior mutabillity] that can be shared between
26/// threads.
27///
28/// [interior mutabillity]: https://doc.rust-lang.org/reference/interior-mutability.html
29///
30/// # Safety
31///
32/// It is safe to send an `Arc<RcuHashMap<T>>` to a non-registered RCU thread. A
33/// non-registered thread may drop an `RcuHashMap<T>` without calling any RCU
34/// primitives since lifetime rules prevent any other thread from accessing an
35/// RCU reference.
36pub struct RcuHashMap<K, V, F = RcuDefaultFlavor>(RawMap<K, V, F>)
37where
38 K: Send + 'static,
39 V: Send + 'static,
40 F: RcuFlavor + 'static;
41
42impl<K, V, F> RcuHashMap<K, V, F>
43where
44 K: Send,
45 V: Send,
46 F: RcuFlavor,
47{
48 /// Creates a new RCU hashmap.
49 pub fn new() -> Result<Arc<Self>> {
50 Ok(Arc::new(Self(RawMap::new()?)))
51 }
52
53 /// Inserts a key-value pair in the hashmap.
54 ///
55 /// If the hashmap did not have this key present, [`None`] is returned.
56 pub fn insert<G>(&self, key: K, value: V, guard: &G) -> Option<Ref<K, V, F>>
57 where
58 K: Send + Eq + Hash,
59 V: Send,
60 G: RcuGuard<Flavor = F>,
61 {
62 let _ = guard;
63
64 // SAFETY: The read-side RCU lock is taken.
65 // SAFETY: The RCU grace period is enforced through the RcuRef.
66 let node = unsafe { self.0.add_replace(key, value) };
67
68 NonNull::new(node).map(Ref::new)
69 }
70
71 /// Returns `true` if the hashmap contains a value for the specified key.
72 pub fn contains<G>(&self, key: &K, guard: &G) -> bool
73 where
74 K: Eq + Hash,
75 G: RcuGuard<Flavor = F>,
76 {
77 let _ = guard;
78
79 // SAFETY: The RCU read-side lock is taken.
80 let mut iter = unsafe { self.0.lookup(key) };
81
82 !iter.get().is_null()
83 }
84
85 /// Returns a reference to the value corresponding to the key.
86 pub fn get<'me, 'guard, G>(&'me self, key: &K, _guard: &'guard G) -> Option<&'guard V>
87 where
88 'me: 'guard,
89 K: Eq + Hash,
90 G: RcuGuard<Flavor = F>,
91 {
92 // SAFETY: The RCU read-side lock is taken.
93 let mut iter = unsafe { self.0.lookup(key) };
94
95 // SAFETY: The node pointer is convertible to a reference is non-null.
96 unsafe { iter.get().as_ref() }.map(|node| &node.value)
97 }
98
99 /// Removes a key from the hashmap, returning the key-value pair if successful.
100 pub fn remove<G>(&self, key: &K, guard: &G) -> Option<Ref<K, V, F>>
101 where
102 K: Send + Eq + Hash,
103 V: Send,
104 G: RcuGuard<Flavor = F>,
105 {
106 let _ = guard;
107
108 // SAFETY: The RCU read-side lock is taken.
109 let mut iter = unsafe { self.0.lookup(key) };
110
111 // SAFETY: The node pointer is convertible to a reference is non-null.
112 let node = match unsafe { iter.get().as_ref() } {
113 None => std::ptr::null_mut(),
114 Some(node) => {
115 // SAFETY: The RCU read-side lock is taken.
116 // SAFETY: The RCU grace period is enforced through RcuRef.
117 unsafe { self.0.del(node.into()) }
118 }
119 };
120
121 NonNull::new(node).map(Ref::new)
122 }
123
124 /// Returns an iterator visiting all key-value pairs in arbitrary order.
125 pub fn iter<'me, 'guard, G>(&'me self, guard: &'guard G) -> Iter<'guard, K, V, F>
126 where
127 'me: 'guard,
128 G: RcuGuard<Flavor = F>,
129 {
130 let _ = guard;
131
132 Iter::new(
133 // SAFETY: The read-side RCU lock is taken.
134 unsafe { self.0.iter() },
135 )
136 }
137}
138
139impl<K, V, F> Drop for RcuHashMap<K, V, F>
140where
141 K: Send + 'static,
142 V: Send + 'static,
143 F: RcuFlavor + 'static,
144{
145 fn drop(&mut self) {
146 let mut raw = self.0.clone();
147
148 F::rcu_cleanup_and_block(Box::new(move |context| {
149 let guard = context.rcu_read_lock();
150
151 // SAFETY: The read-side RCU lock is taken.
152 unsafe { raw.del_all() }
153 .iter()
154 .copied()
155 .map(Ref::<K, V, F>::new)
156 .collect::<Vec<_>>()
157 .safe_cleanup();
158
159 drop(guard);
160
161 // SAFETY: The read-side RCU lock is not taken.
162 // SAFETY: We are a registered RCU read-side thread.
163 unsafe { raw.destroy() };
164 }));
165 }
166}