Skip to main content

starnix_rcu/
rcu_hash_map.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fuchsia_rcu::{RcuDroppable, RcuReadScope};
6use fuchsia_rcu_collections::rcu_raw_hash_map::{InsertionResult, RcuRawHashMap};
7use starnix_sync::{DynamicLockDepMutex, LockDepGuard, LockLevel, RcuHashMapLock};
8use std::borrow::Borrow;
9use std::hash::{BuildHasher, Hash};
10use std::marker::PhantomData;
11use std::ops::Deref;
12
13/// A concurrent hash map that uses RCU for read synchronization and a mutex with a dynamically
14/// specified lock level for write synchronization.
15#[derive(RcuDroppable)]
16pub struct DynamicRcuHashMap<K, V, S = rapidhash::RapidBuildHasher>
17where
18    K: Eq + Hash + Clone + RcuDroppable + Sync,
19    V: Clone + RcuDroppable + Sync,
20    S: BuildHasher + Send + Sync + 'static,
21{
22    map: RcuRawHashMap<K, V, S>,
23    mutex: DynamicLockDepMutex<()>,
24}
25
26impl<K, V> Default for DynamicRcuHashMap<K, V, rapidhash::RapidBuildHasher>
27where
28    K: Eq + Hash + Clone + RcuDroppable + Sync,
29    V: Clone + RcuDroppable + Sync,
30{
31    fn default() -> Self {
32        Self::new::<RcuHashMapLock>()
33    }
34}
35
36impl<K, V> DynamicRcuHashMap<K, V, rapidhash::RapidBuildHasher>
37where
38    K: Eq + Hash + Clone + RcuDroppable + Sync,
39    V: Clone + RcuDroppable + Sync,
40{
41    /// Creates a new hash map with the given LockDep lock level.
42    pub fn new<L: LockLevel>() -> Self {
43        Self { map: Default::default(), mutex: DynamicLockDepMutex::new::<L>(()) }
44    }
45}
46
47impl<K, V, S> DynamicRcuHashMap<K, V, S>
48where
49    K: Eq + Hash + Clone + RcuDroppable + Sync,
50    V: Clone + RcuDroppable + Sync,
51    S: BuildHasher + Send + Sync + 'static,
52{
53    /// Creates a new hash map with the given capacity, hasher, and LockDep lock level.
54    pub fn with_capacity_and_hasher<L: LockLevel>(capacity: usize, hash_builder: S) -> Self {
55        Self {
56            map: RcuRawHashMap::with_capacity_and_hasher(capacity, hash_builder),
57            mutex: DynamicLockDepMutex::new::<L>(()),
58        }
59    }
60
61    /// Creates a new hash map with the given hasher and LockDep lock level.
62    pub fn with_hasher<L: LockLevel>(hash_builder: S) -> Self {
63        Self {
64            map: RcuRawHashMap::with_hasher(hash_builder),
65            mutex: DynamicLockDepMutex::new::<L>(()),
66        }
67    }
68
69    /// Returns a reference to the value associated with the given key, if it exists.
70    ///
71    /// The returned reference is bound to the lifetime of the `RcuReadScope`.
72    pub fn get<'a, Q>(&self, scope: &'a RcuReadScope, key: &Q) -> Option<&'a V>
73    where
74        K: Borrow<Q>,
75        Q: ?Sized + Hash + Eq,
76    {
77        self.map.get(scope, key)
78    }
79
80    /// Locks the map for exclusive access, returning a guard that allows mutation.
81    pub fn lock(&self) -> RcuHashMapGuard<'_, K, V, S> {
82        RcuHashMapGuard { map: &self.map, _guard: self.mutex.lock() }
83    }
84
85    /// Inserts a key-value pair into the map, returning the old value if the key was already present.
86    pub fn insert(&self, key: K, value: V) -> Option<V> {
87        self.lock().insert(key, value)
88    }
89
90    /// Removes a key from the map, returning the value if the key was present.
91    pub fn remove<Q>(&self, key: &Q) -> Option<V>
92    where
93        K: Borrow<Q>,
94        Q: ?Sized + Hash + Eq,
95    {
96        self.lock().remove(key)
97    }
98
99    /// Returns an iterator over the map's entries.
100    pub fn iter<'a>(&'a self, scope: &'a RcuReadScope) -> impl Iterator<Item = (&'a K, &'a V)> {
101        let mut cursor = self.map.cursor(scope);
102        std::iter::from_fn(move || {
103            let current = cursor.current();
104            if current.is_some() {
105                cursor.advance();
106            }
107            current
108        })
109    }
110
111    /// Returns an iterator over the map's keys.
112    pub fn keys<'a>(&'a self, scope: &'a RcuReadScope) -> impl Iterator<Item = &'a K> {
113        self.iter(scope).map(|(k, _)| k)
114    }
115
116    /// Returns an iterator over the map's values.
117    pub fn values<'a>(&'a self, scope: &'a RcuReadScope) -> impl Iterator<Item = &'a V> {
118        self.iter(scope).map(|(_, v)| v)
119    }
120
121    /// Returns the number of entries in the map.
122    pub fn len(&self) -> usize {
123        self.map.len()
124    }
125
126    /// Returns true if the map contains no entries.
127    pub fn is_empty(&self) -> bool {
128        self.map.is_empty()
129    }
130
131    /// Removes all entries from the map while preserving active RCU reader snapshots.
132    pub fn clear(&self) {
133        self.lock().clear();
134    }
135}
136
137impl<K, V, S> starnix_sync::MutexLike for DynamicRcuHashMap<K, V, S>
138where
139    K: Eq + Hash + Clone + RcuDroppable + Sync,
140    V: Clone + RcuDroppable + Sync,
141    S: BuildHasher + Send + Sync + 'static,
142{
143    type Guard<'a>
144        = RcuHashMapGuard<'a, K, V, S>
145    where
146        Self: 'a;
147    type Context = <DynamicLockDepMutex<()> as starnix_sync::MutexLike>::Context;
148
149    #[inline(always)]
150    fn context() -> Self::Context {
151        <DynamicLockDepMutex<()> as starnix_sync::MutexLike>::context()
152    }
153
154    #[inline(always)]
155    fn lock(&self, context: &mut Self::Context) -> Self::Guard<'_> {
156        RcuHashMapGuard {
157            map: &self.map,
158            _guard: <DynamicLockDepMutex<()> as starnix_sync::MutexLike>::lock(
159                &self.mutex,
160                context,
161            ),
162        }
163    }
164}
165
166impl<K, V, S> fuchsia_sync::ResetDependencies for DynamicRcuHashMap<K, V, S>
167where
168    K: Eq + Hash + Clone + RcuDroppable + Sync,
169    V: Clone + RcuDroppable + Sync,
170    S: BuildHasher + Send + Sync + 'static,
171{
172    #[inline(always)]
173    unsafe fn reset_dependencies(&self) {
174        // SAFETY: The caller must uphold the safety requirements of `ResetDependencies`.
175        unsafe {
176            self.mutex.reset_dependencies();
177        }
178    }
179}
180
181// TODO(https://fxbug.dev/482462174): switch back to #[derive(Debug)]
182impl<K, V, S> std::fmt::Debug for DynamicRcuHashMap<K, V, S>
183where
184    K: Eq + Hash + std::fmt::Debug + Clone + RcuDroppable + Sync,
185    V: std::fmt::Debug + Clone + RcuDroppable + Sync,
186    S: BuildHasher + Send + Sync + 'static,
187{
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        f.debug_struct("DynamicRcuHashMap").field("map", &self.map).finish()
190    }
191}
192
193/// A concurrent hash map that uses RCU for read synchronization and a mutex with a statically
194/// typed LockDep level for write synchronization.
195///
196/// By default, this map uses `rapidhash::RapidBuildHasher`, which provides high performance.
197/// However, if this map holds keys which may be attacker-controlled, consider using
198/// `std::collections::hash_map::RandomState` instead.
199#[derive(RcuDroppable)]
200pub struct RcuHashMap<K, V, L = RcuHashMapLock, S = rapidhash::RapidBuildHasher>
201where
202    K: Eq + Hash + Clone + RcuDroppable + Sync,
203    V: Clone + RcuDroppable + Sync,
204    L: LockLevel,
205    S: BuildHasher + Send + Sync + 'static,
206{
207    inner: DynamicRcuHashMap<K, V, S>,
208    _level: PhantomData<L>,
209}
210
211impl<K, V> Default for RcuHashMap<K, V, RcuHashMapLock, rapidhash::RapidBuildHasher>
212where
213    K: Eq + Hash + Clone + RcuDroppable + Sync,
214    V: Clone + RcuDroppable + Sync,
215{
216    fn default() -> Self {
217        Self::new()
218    }
219}
220
221impl<K, V, L> RcuHashMap<K, V, L, rapidhash::RapidBuildHasher>
222where
223    K: Eq + Hash + Clone + RcuDroppable + Sync,
224    V: Clone + RcuDroppable + Sync,
225    L: LockLevel,
226{
227    /// Creates a new hash map.
228    pub fn new() -> Self {
229        Self { inner: DynamicRcuHashMap::new::<L>(), _level: PhantomData }
230    }
231}
232
233impl<K, V, S> RcuHashMap<K, V, RcuHashMapLock, S>
234where
235    K: Eq + Hash + Clone + RcuDroppable + Sync,
236    V: Clone + RcuDroppable + Sync,
237    S: BuildHasher + Send + Sync + 'static,
238{
239    /// Creates a new hash map with the given capacity and hasher.
240    pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Self {
241        Self {
242            inner: DynamicRcuHashMap::with_capacity_and_hasher::<RcuHashMapLock>(
243                capacity,
244                hash_builder,
245            ),
246            _level: PhantomData,
247        }
248    }
249
250    /// Creates a new hash map with the given hasher.
251    pub fn with_hasher(hash_builder: S) -> Self {
252        Self {
253            inner: DynamicRcuHashMap::with_hasher::<RcuHashMapLock>(hash_builder),
254            _level: PhantomData,
255        }
256    }
257}
258
259impl<K, V, L, S> Deref for RcuHashMap<K, V, L, S>
260where
261    K: Eq + Hash + Clone + RcuDroppable + Sync,
262    V: Clone + RcuDroppable + Sync,
263    L: LockLevel,
264    S: BuildHasher + Send + Sync + 'static,
265{
266    type Target = DynamicRcuHashMap<K, V, S>;
267
268    fn deref(&self) -> &Self::Target {
269        &self.inner
270    }
271}
272
273impl<K, V, L, S> starnix_sync::MutexLike for RcuHashMap<K, V, L, S>
274where
275    K: Eq + Hash + Clone + RcuDroppable + Sync,
276    V: Clone + RcuDroppable + Sync,
277    L: LockLevel,
278    S: BuildHasher + Send + Sync + 'static,
279{
280    type Guard<'a>
281        = RcuHashMapGuard<'a, K, V, S>
282    where
283        Self: 'a;
284    type Context = <DynamicRcuHashMap<K, V, S> as starnix_sync::MutexLike>::Context;
285
286    #[inline(always)]
287    fn context() -> Self::Context {
288        <DynamicRcuHashMap<K, V, S> as starnix_sync::MutexLike>::context()
289    }
290
291    #[inline(always)]
292    fn lock(&self, context: &mut Self::Context) -> Self::Guard<'_> {
293        <DynamicRcuHashMap<K, V, S> as starnix_sync::MutexLike>::lock(&self.inner, context)
294    }
295}
296
297impl<K, V, L, S> fuchsia_sync::ResetDependencies for RcuHashMap<K, V, L, S>
298where
299    K: Eq + Hash + Clone + RcuDroppable + Sync,
300    V: Clone + RcuDroppable + Sync,
301    L: LockLevel,
302    S: BuildHasher + Send + Sync + 'static,
303{
304    #[inline(always)]
305    unsafe fn reset_dependencies(&self) {
306        // SAFETY: The caller must uphold the safety requirements of `ResetDependencies`.
307        unsafe {
308            self.inner.reset_dependencies();
309        }
310    }
311}
312
313// TODO(https://fxbug.dev/482462174): switch back to #[derive(Debug)]
314impl<K, V, L, S> std::fmt::Debug for RcuHashMap<K, V, L, S>
315where
316    K: Eq + Hash + std::fmt::Debug + Clone + RcuDroppable + Sync,
317    V: std::fmt::Debug + Clone + RcuDroppable + Sync,
318    L: LockLevel,
319    S: BuildHasher + Send + Sync + 'static,
320{
321    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
322        f.debug_struct("RcuHashMap").field("map", &self.inner.map).finish()
323    }
324}
325
326/// A guard that provides exclusive access to the `RcuHashMap`.
327pub struct RcuHashMapGuard<'a, K, V, S = rapidhash::RapidBuildHasher>
328where
329    K: Eq + Hash + Clone + RcuDroppable + Sync,
330    V: Clone + RcuDroppable + Sync,
331    S: BuildHasher + Send + Sync + 'static,
332{
333    map: &'a RcuRawHashMap<K, V, S>,
334    _guard: LockDepGuard<'a, ()>,
335}
336
337impl<'a, K, V, S> RcuHashMapGuard<'a, K, V, S>
338where
339    K: Eq + Hash + Clone + RcuDroppable + Sync,
340    V: Clone + RcuDroppable + Sync,
341    S: BuildHasher + Send + Sync + 'static,
342{
343    /// Returns a copy (clone) of the value associated with the given key, if it exists.
344    pub fn get<Q>(&self, key: &Q) -> Option<V>
345    where
346        K: Borrow<Q>,
347        Q: ?Sized + Hash + Eq,
348    {
349        let scope = RcuReadScope::new();
350        self.map.get(&scope, key).cloned()
351    }
352
353    /// Inserts a key-value pair into the map.
354    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
355        let scope = RcuReadScope::new();
356        // SAFETY: We have exclusive access to the map because we have exclusive access to the mutex.
357        match unsafe { self.map.insert(&scope, key, value) } {
358            InsertionResult::Inserted(_) => None,
359            InsertionResult::Updated(old_value) => Some(old_value),
360        }
361    }
362
363    /// Removes a key from the map.
364    pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
365    where
366        K: Borrow<Q>,
367        Q: ?Sized + Hash + Eq,
368    {
369        // SAFETY: We have exclusive access to the map because we have exclusive access to the mutex.
370        unsafe { self.map.remove(key) }
371    }
372
373    /// Removes all entries from the map while preserving active RCU reader snapshots.
374    pub fn clear(&mut self) {
375        // SAFETY: Exclusive access to the map is guaranteed by the held mutex guard.
376        unsafe {
377            self.map.clear();
378        }
379    }
380
381    /// Returns the number of entries in the map.
382    pub fn len(&self) -> usize {
383        self.map.len()
384    }
385
386    /// Returns true if the map contains no entries.
387    pub fn is_empty(&self) -> bool {
388        self.map.is_empty()
389    }
390
391    /// Returns an iterator over the map's entries under the provided RCU read scope.
392    pub fn iter<'b>(&'b self, scope: &'b RcuReadScope) -> impl Iterator<Item = (&'b K, &'b V)> {
393        self.map.iter(scope)
394    }
395
396    /// Removes all values from the map and returns them.
397    pub fn drain<'b>(&'b mut self) -> impl Iterator<Item = (K, V)> + 'b {
398        let scope = RcuReadScope::new();
399        // We collect the keys first because we cannot iterate and modify the map at the same time.
400        #[allow(clippy::needless_collect)]
401        let keys: Vec<_> = self.map.keys(&scope).map(Clone::clone).collect();
402        keys.into_iter().filter_map(move |k| self.remove(&k).map(|v| (k, v)))
403    }
404
405    /// Returns true if the map contains a value for the specified key.
406    pub fn contains_key<Q>(&self, key: &Q) -> bool
407    where
408        K: Borrow<Q>,
409        Q: ?Sized + Hash + Eq,
410    {
411        self.get(key).is_some()
412    }
413
414    /// Gets the given key's corresponding entry in the map for in-place manipulation.
415    pub fn entry<'b>(&'b mut self, key: K) -> Entry<'b, 'a, K, V, S> {
416        if self.get(&key).is_some() {
417            Entry::Occupied(OccupiedEntry { guard: self, key })
418        } else {
419            Entry::Vacant(VacantEntry { guard: self, key })
420        }
421    }
422}
423
424/// A view into a single entry in the map, which may either be vacant or occupied.
425pub enum Entry<'b, 'a, K, V, S = rapidhash::RapidBuildHasher>
426where
427    K: Eq + Hash + Clone + RcuDroppable + Sync,
428    V: Clone + RcuDroppable + Sync,
429    S: BuildHasher + Send + Sync + 'static,
430{
431    /// An occupied entry.
432    Occupied(OccupiedEntry<'b, 'a, K, V, S>),
433    /// A vacant entry.
434    Vacant(VacantEntry<'b, 'a, K, V, S>),
435}
436
437impl<'b, 'a, K, V, S> Entry<'b, 'a, K, V, S>
438where
439    K: Eq + Hash + Clone + RcuDroppable + Sync,
440    V: Clone + RcuDroppable + Sync,
441    S: BuildHasher + Send + Sync + 'static,
442{
443    /// Ensures a value is in the entry by inserting the result of the default function if empty,
444    /// and returns an occupied entry.
445    pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> OccupiedEntry<'b, 'a, K, V, S> {
446        match self {
447            Entry::Occupied(entry) => entry,
448            Entry::Vacant(entry) => entry.insert_entry(default()),
449        }
450    }
451}
452
453/// A view into an occupied entry in a `RcuHashMap`.
454pub struct OccupiedEntry<'b, 'a, K, V, S = rapidhash::RapidBuildHasher>
455where
456    K: Eq + Hash + Clone + RcuDroppable + Sync,
457    V: Clone + RcuDroppable + Sync,
458    S: BuildHasher + Send + Sync + 'static,
459{
460    guard: &'b mut RcuHashMapGuard<'a, K, V, S>,
461    key: K,
462}
463
464impl<K, V, S> OccupiedEntry<'_, '_, K, V, S>
465where
466    K: Eq + Hash + Clone + RcuDroppable + Sync,
467    V: Clone + RcuDroppable + Sync,
468    S: BuildHasher + Send + Sync + 'static,
469{
470    /// Gets a copy (clone) of the value in the entry.
471    pub fn get(&self) -> V {
472        self.guard.get(&self.key).unwrap()
473    }
474
475    /// Sets the value of the entry, returning the old value.
476    pub fn insert(&mut self, value: V) -> V {
477        self.guard.insert(self.key.clone(), value).unwrap()
478    }
479
480    /// Removes the entry from the map, returning the value.
481    pub fn remove(self) -> V {
482        self.guard.remove(&self.key).unwrap()
483    }
484}
485
486/// A view into a vacant entry in a `RcuHashMap`.
487pub struct VacantEntry<'b, 'a, K, V, S = rapidhash::RapidBuildHasher>
488where
489    K: Eq + Hash + Clone + RcuDroppable + Sync,
490    V: Clone + RcuDroppable + Sync,
491    S: BuildHasher + Send + Sync + 'static,
492{
493    guard: &'b mut RcuHashMapGuard<'a, K, V, S>,
494    key: K,
495}
496
497impl<'b, 'a, K, V, S> VacantEntry<'b, 'a, K, V, S>
498where
499    K: Eq + Hash + Clone + RcuDroppable + Sync,
500    V: Clone + RcuDroppable + Sync,
501    S: BuildHasher + Send + Sync + 'static,
502{
503    /// Sets the value of the entry with the VacantEntry's key.
504    pub fn insert(self, value: V) {
505        self.guard.insert(self.key, value);
506    }
507
508    /// Sets the value of the entry with the VacantEntry's key, and returns an occupied entry.
509    pub fn insert_entry(self, value: V) -> OccupiedEntry<'b, 'a, K, V, S> {
510        self.guard.insert(self.key.clone(), value);
511        OccupiedEntry { guard: self.guard, key: self.key }
512    }
513}
514
515#[cfg(test)]
516mod tests {
517    use super::*;
518    use fuchsia_rcu::rcu_run_callbacks;
519
520    #[test]
521    fn test_rcu_hash_map_custom_hasher() {
522        use std::collections::hash_map::DefaultHasher;
523        use std::hash::BuildHasherDefault;
524        let hasher = BuildHasherDefault::<DefaultHasher>::default();
525        let map = RcuHashMap::with_capacity_and_hasher(10, hasher);
526        let mut guard = map.lock();
527        guard.insert(1, 10);
528        assert_eq!(guard.get(&1), Some(10));
529    }
530
531    #[test]
532    fn test_rcu_hash_map_insert_and_get() {
533        let map = RcuHashMap::<i32, i32>::default();
534        let mut guard = map.lock();
535        let scope = RcuReadScope::new();
536
537        guard.insert(1, 10);
538        guard.insert(2, 20);
539
540        assert_eq!(guard.get(&1), Some(10));
541        assert_eq!(guard.get(&2), Some(20));
542        assert_eq!(guard.get(&3), None);
543
544        // Verify we can read without the lock too
545        drop(guard);
546        assert_eq!(map.get(&scope, &1), Some(&10));
547        assert_eq!(map.get(&scope, &2), Some(&20));
548
549        drop(scope);
550        rcu_run_callbacks();
551    }
552
553    #[test]
554    fn test_rcu_hash_map_update() {
555        let map = RcuHashMap::<i32, i32>::default();
556        let mut guard = map.lock();
557        let scope = RcuReadScope::new();
558
559        guard.insert(1, 10);
560        assert_eq!(guard.get(&1), Some(10));
561
562        guard.insert(1, 20);
563        assert_eq!(guard.get(&1), Some(20));
564
565        drop(guard);
566        assert_eq!(map.get(&scope, &1), Some(&20));
567
568        drop(scope);
569        rcu_run_callbacks();
570    }
571
572    #[test]
573    fn test_rcu_hash_map_remove() {
574        let map = RcuHashMap::<i32, i32>::default();
575        let mut guard = map.lock();
576        let scope = RcuReadScope::new();
577
578        guard.insert(1, 10);
579        assert_eq!(guard.get(&1), Some(10));
580
581        guard.remove(&1);
582        assert_eq!(guard.get(&1), None);
583
584        drop(guard);
585        assert_eq!(map.get(&scope, &1), None);
586
587        drop(scope);
588        rcu_run_callbacks();
589    }
590
591    #[test]
592    fn test_rcu_hash_map_entry_api() {
593        let map = RcuHashMap::<i32, i32>::default();
594        let mut guard = map.lock();
595
596        // Vacant entry
597        match guard.entry(1) {
598            Entry::Vacant(e) => e.insert(10),
599            Entry::Occupied(_) => panic!("Should be vacant"),
600        }
601        assert_eq!(guard.get(&1), Some(10));
602
603        // Occupied entry
604        match guard.entry(1) {
605            Entry::Occupied(mut e) => {
606                assert_eq!(e.get(), 10);
607                e.insert(20);
608            }
609            Entry::Vacant(_) => panic!("Should be occupied"),
610        }
611        assert_eq!(guard.get(&1), Some(20));
612
613        drop(guard);
614        rcu_run_callbacks();
615    }
616
617    #[test]
618    fn test_rcu_hash_map_iter() {
619        let map = RcuHashMap::<i32, i32>::default();
620        let scope = RcuReadScope::new();
621        map.insert(1, 10);
622        map.insert(2, 20);
623        map.insert(3, 30);
624
625        let mut items: Vec<_> = map.iter(&scope).collect();
626        items.sort_by_key(|(k, _)| **k);
627        assert_eq!(items, vec![(&1, &10), (&2, &20), (&3, &30)]);
628    }
629
630    #[test]
631    fn test_rcu_hash_map_keys() {
632        let map = RcuHashMap::<i32, i32>::default();
633        let scope = RcuReadScope::new();
634        map.insert(1, 10);
635        map.insert(2, 20);
636        map.insert(3, 30);
637
638        let mut keys: Vec<_> = map.keys(&scope).collect();
639        keys.sort();
640        assert_eq!(keys, vec![&1, &2, &3]);
641    }
642
643    #[test]
644    fn test_rcu_hash_map_len() {
645        let map = RcuHashMap::<i32, i32>::default();
646        map.insert(1, 10);
647        map.insert(2, 20);
648        map.insert(3, 30);
649
650        assert_eq!(map.len(), 3);
651    }
652
653    #[test]
654    fn test_rcu_hash_map_or_insert_with() {
655        let map = RcuHashMap::<i32, i32>::default();
656        let mut guard = map.lock();
657
658        // test or_insert_with
659        guard.entry(1).or_insert_with(|| 10);
660        assert!(guard.contains_key(&1));
661        assert_eq!(guard.get(&1), Some(10));
662
663        // test or_insert_with existing
664        guard.entry(1).or_insert_with(|| 20);
665        assert_eq!(guard.get(&1), Some(10));
666
667        // test OccupiedEntry::remove
668        match guard.entry(1) {
669            Entry::Occupied(e) => {
670                assert_eq!(e.remove(), 10);
671            }
672            Entry::Vacant(_) => panic!("Should be occupied"),
673        }
674        assert!(!guard.contains_key(&1));
675    }
676
677    #[test]
678    fn test_rcu_hash_map_drain() {
679        let map = RcuHashMap::<i32, i32>::default();
680        let mut guard = map.lock();
681
682        guard.insert(1, 10);
683        guard.insert(2, 20);
684        guard.insert(3, 30);
685
686        let mut items: Vec<_> = guard.drain().collect();
687        items.sort_by_key(|(k, _)| *k);
688        assert_eq!(items, vec![(1, 10), (2, 20), (3, 30)]);
689
690        assert!(!guard.contains_key(&1));
691        assert!(!guard.contains_key(&2));
692        assert!(!guard.contains_key(&3));
693    }
694
695    #[test]
696    fn test_rcu_hash_map_capacity_zero() {
697        use std::collections::hash_map::RandomState;
698        let map = RcuHashMap::<i32, i32, RcuHashMapLock, RandomState>::with_capacity_and_hasher(
699            0,
700            RandomState::new(),
701        );
702        let mut guard = map.lock();
703
704        assert_eq!(guard.get(&1), None);
705
706        guard.insert(1, 10);
707        assert_eq!(guard.get(&1), Some(10));
708
709        guard.remove(&1);
710        assert_eq!(guard.get(&1), None);
711    }
712}