atom_box/
lib.rs

1//! # Atom Box
2//!
3//! This crate provides a safe idiomatic Rust API for an Atomic Box with safe memory
4//! reclamation when used in multi-threaded concurrent lock-free data structures.
5//!
6//! Under the covers it uses Hazard Pointers to ensure memory is only reclaimed when all references
7//! are dropped.
8//!
9//! The main type provided is the `AtomBox`.
10//!
11//! # Examples
12//!
13//! ```
14//! use atom_box::AtomBox;
15//! use std::{sync::Arc, thread};
16//!
17//! const ITERATIONS: usize = 1000;
18//!
19//! let atom_box1 = Arc::new(AtomBox::new(0));
20//! let atom_box2 = Arc::new(AtomBox::new(0));
21//!
22//! let a_box1 = atom_box1.clone();
23//! let handle1 = thread::spawn(move || {
24//!     let mut current_value = 0;
25//!     for _ in 1..=ITERATIONS {
26//!         let new_value = a_box1.load();
27//!         assert!(*new_value >= current_value, "Value should not decrease");
28//!         current_value = *new_value;
29//!     }
30//! });
31//!
32//! let a_box1 = atom_box1.clone();
33//! let a_box2 = atom_box2.clone();
34//! let handle2 = thread::spawn(move || {
35//!     for i in 1..=ITERATIONS {
36//!         let guard1 = a_box1.swap(i);
37//!         let value1 = *guard1;
38//!         let guard2 = a_box2.swap_from_guard(guard1);
39//!         assert!(
40//!             *guard2 <= value1,
41//!             "Value in first box should be greater than or equal to value in second box"
42//!         );
43//!     }
44//! });
45//!
46//! handle1.join().unwrap();
47//! handle2.join().unwrap();
48//! ```
49
50#![no_std]
51#![warn(missing_docs)]
52extern crate alloc;
53#[cfg(feature = "std")]
54extern crate std;
55use crate::sync::{AtomicPtr, Ordering};
56use core::ops::Deref;
57
58pub mod domain;
59mod sync;
60
61use crate::domain::{Domain, HazardPointer};
62use alloc::boxed::Box;
63
64#[cfg(not(loom))]
65const SHARED_DOMAIN_ID: usize = 0;
66
67#[cfg(not(loom))]
68static SHARED_DOMAIN: Domain<SHARED_DOMAIN_ID> = Domain::default();
69
70mod macros {
71    // The loom atomics do not have const constructors. So we cannot use them in const functions.
72    // This macro enables us to create a const function in normal compilation and a non const
73    // function when compiling for loom.
74    macro_rules! conditional_const {
75        ($doc_comment:expr, $visibility:vis, $( $token:tt )*) => {
76            #[doc = $doc_comment]
77            #[cfg(not(loom))]
78            $visibility const $( $token )*
79            #[cfg(loom)]
80            $visibility $( $token )*
81        };
82    }
83    pub(crate) use conditional_const;
84}
85
86/// A box which can safely be shared between threads and atomically updated.
87///
88/// Memory will be safely reclaimed after all threads have dropped their references to any give
89/// value.
90///
91/// # Example
92///
93/// ```
94/// use atom_box::AtomBox;
95/// use std::thread;
96///
97/// const ITERATIONS: usize = 1000;
98///
99/// let atom_box1: &'static _ = AtomBox::new_static(0);
100/// let atom_box2: &'static _ = AtomBox::new_static(0);
101///
102/// let handle1 = thread::spawn(move || {
103///     let mut current_value = 0;
104///     for _ in 1..=ITERATIONS {
105///         let new_value = atom_box1.load();
106///         assert!(*new_value >= current_value, "Value should not decrease");
107///         current_value = *new_value;
108///     }
109/// });
110///
111/// let handle2 = thread::spawn(move || {
112///     for i in 1..=ITERATIONS {
113///         let guard1 = atom_box1.swap(i);
114///         let value1 = *guard1;
115///         let guard2 = atom_box2.swap_from_guard(guard1);
116///         assert!(
117///             *guard2 <= value1,
118///             "Value in first box should be greater than or equal to value in second box"
119///         );
120///     }
121/// });
122///
123/// handle1.join().unwrap();
124/// handle2.join().unwrap();
125/// ```
126#[derive(Debug)]
127pub struct AtomBox<'domain, T, const DOMAIN_ID: usize> {
128    ptr: AtomicPtr<T>,
129    domain: &'domain Domain<DOMAIN_ID>,
130}
131
132#[cfg(not(loom))]
133impl<T> AtomBox<'static, T, SHARED_DOMAIN_ID> {
134    /// Creates a new `AtomBox` associated with the shared (global) domain.
135    ///
136    /// # Example
137    ///
138    /// ```
139    /// use atom_box::AtomBox;
140    ///
141    /// let atom_box = AtomBox::new("Hello");
142    ///
143    /// let value = atom_box.load();
144    /// assert_eq!(*value, "Hello");
145    ///
146    /// atom_box.store("World");
147    /// let value = atom_box.load();
148    /// assert_eq!(*value, "World");
149    /// ```
150    pub fn new(value: T) -> Self {
151        let ptr = AtomicPtr::new(Box::into_raw(Box::new(value)));
152        Self {
153            ptr,
154            domain: &SHARED_DOMAIN,
155        }
156    }
157
158    /// Creates a new `AtomBox` with a static lifetime.
159    ///
160    /// A convenience constructor for `Box::leak(Box::new(Self::new(value)))`.
161    ///
162    /// # Example
163    ///
164    /// ```
165    /// use atom_box::AtomBox;
166    /// let atom_box: &'static _ = AtomBox::new_static(50);
167    /// let value = atom_box.load();
168    ///
169    /// assert_eq!(
170    ///     *value, 50,
171    ///     "We are able to get the original value by loading it"
172    /// );
173    /// let handle1 = std::thread::spawn(move || {
174    ///     let h_box = atom_box;
175    ///     let value = h_box.load();
176    ///     assert_eq!(
177    ///         *value, 50,
178    ///         "The value should be accessible in multiple threads"
179    ///     );
180    /// });
181    /// let handle2 = std::thread::spawn(move || {
182    ///     let h_box = atom_box;
183    ///     let value = h_box.load();
184    ///     assert_eq!(
185    ///         *value, 50,
186    ///         "The value should be accessible in multiple threads"
187    ///     );
188    /// });
189    /// handle1.join().unwrap();
190    /// handle2.join().unwrap();
191    /// ```
192    pub fn new_static(value: T) -> &'static mut Self {
193        Box::leak(Box::new(Self::new(value)))
194    }
195}
196
197impl<'domain, T, const DOMAIN_ID: usize> AtomBox<'domain, T, DOMAIN_ID> {
198    /// Creates a new `AtomBox` and assoicates it with the given domain.
199    ///
200    /// # Example
201    ///
202    /// ```
203    /// use atom_box::{AtomBox, domain::Domain, domain::ReclaimStrategy};
204    ///
205    /// const CUSTOM_DOMAIN_ID: usize = 42;
206    /// static CUSTOM_DOMAIN: Domain<CUSTOM_DOMAIN_ID> = Domain::new(ReclaimStrategy::Eager);
207    ///
208    /// let atom_box = AtomBox::new_with_domain("Hello World", &CUSTOM_DOMAIN);
209    /// assert_eq!(*atom_box.load(), "Hello World");
210    /// ```
211    pub fn new_with_domain(value: T, domain: &'domain Domain<DOMAIN_ID>) -> Self {
212        let ptr = AtomicPtr::new(Box::into_raw(Box::new(value)));
213        Self { ptr, domain }
214    }
215
216    /// Loads the value stored in the `AtomBox`.
217    ///
218    /// Returns a `LoadGuard` which can be dereferenced into the value.
219    ///
220    /// # Example
221    ///
222    /// ```
223    /// use atom_box::AtomBox;
224    ///
225    /// let atom_box = AtomBox::new("Hello World");
226    ///
227    /// let value = atom_box.load();
228    /// assert_eq!(*value, "Hello World");
229    /// ```
230    pub fn load(&self) -> LoadGuard<'domain, T, DOMAIN_ID> {
231        let haz_ptr = self.domain.acquire_haz_ptr();
232        // load pointer
233        let mut original_ptr = self.ptr.load(Ordering::Relaxed);
234
235        let ptr = loop {
236            // protect pointer
237            haz_ptr.protect(original_ptr as *mut usize);
238
239            core::sync::atomic::fence(Ordering::SeqCst);
240
241            // check pointer
242            let current_ptr = self.ptr.load(Ordering::Acquire);
243            if current_ptr == original_ptr {
244                // The pointer is the same, we have successfully protected its value.
245                break current_ptr;
246            }
247            haz_ptr.reset();
248            original_ptr = current_ptr;
249        };
250        LoadGuard {
251            ptr,
252            domain: self.domain,
253            haz_ptr: Some(haz_ptr),
254        }
255    }
256
257    /// Stores a new value in the `AtomBox`
258    ///
259    /// # Example
260    ///
261    /// ```
262    /// use atom_box::AtomBox;
263    ///
264    /// let atom_box = AtomBox::new("Hello");
265    /// atom_box.store("World");
266    ///
267    /// let value = atom_box.load();
268    /// assert_eq!(*value, "World");
269    /// ```
270    pub fn store(&self, value: T) {
271        let _ = self.swap(value);
272    }
273
274    /// Stores the value protected by the `StoreGuard` in the `AtomBox`
275    ///
276    /// # Panics
277    ///
278    /// Panics if the guard is associated with a different domain.
279    ///
280    /// # Example
281    ///
282    /// ```
283    /// use atom_box::AtomBox;
284    ///
285    /// let atom_box1 = AtomBox::new("Hello");
286    /// let atom_box2 = AtomBox::new("World");
287    ///
288    /// let guard = atom_box1.swap("Bye Bye");
289    ///
290    /// atom_box2.store_from_guard(guard);
291    /// let value = atom_box2.load();
292    /// assert_eq!(*value, "Hello");
293    /// ```
294    pub fn store_from_guard(&self, value: StoreGuard<'domain, T, DOMAIN_ID>) {
295        let _ = self.swap_from_guard(value);
296    }
297
298    /// Stores the value into the `AtomBox` and returns a `StoreGuard` which dereferences into the
299    /// previous value.
300    ///
301    /// **Note:** This method is only available on platforms that support atomic operations on
302    /// pointers.
303    ///
304    /// # Example
305    ///
306    /// ```
307    /// use atom_box::AtomBox;
308    ///
309    /// let atom_box = AtomBox::new("Hello World");
310    ///
311    /// let guard = atom_box.swap("Bye Bye");
312    /// assert_eq!(*guard, "Hello World");
313    /// ```
314    pub fn swap(&self, new_value: T) -> StoreGuard<'domain, T, DOMAIN_ID> {
315        let new_ptr = Box::into_raw(Box::new(new_value));
316        let old_ptr = self.ptr.swap(new_ptr, Ordering::AcqRel);
317        StoreGuard {
318            ptr: old_ptr,
319            domain: self.domain,
320        }
321    }
322
323    /// Stores the value into the `AtomBox` and returns a `StoreGuard` which dereferences into the
324    /// previous value.
325    ///
326    /// **Note:** This method is only available on platforms that support atomic operations on
327    /// pointers.
328    ///
329    /// # Panics
330    ///
331    /// Panics if the guard is associated with a different domain.
332    ///
333    /// # Example
334    ///
335    /// ```
336    /// use atom_box::AtomBox;
337    ///
338    /// let atom_box1 = AtomBox::new("Hello");
339    /// let atom_box2 = AtomBox::new("World");
340    ///
341    /// let guard1 = atom_box1.swap("Bye Bye");
342    ///
343    /// let guard2 = atom_box2.swap_from_guard(guard1);
344    /// assert_eq!(*guard2, "World");
345    /// ```
346    ///
347    /// The following example will fail to compile.
348    ///
349    /// ```compile_fail
350    /// use atom_box::{AtomBox, domain::{Domain, ReclaimStrategy}};
351    ///
352    /// const CUSTOM_DOMAIN_ID: usize = 42;
353    /// static CUSTOM_DOMAIN: Domain<CUSTOM_DOMAIN_ID> = Domain::new(ReclaimStrategy::Eager);
354    ///
355    /// let atom_box1 = AtomBox::new_with_domain("Hello", &CUSTOM_DOMAIN);
356    /// let atom_box2 = AtomBox::new("World");
357    ///
358    /// let guard = atom_box1.swap("Bye bye");
359    /// atom_box2.swap_from_guard(guard);
360    /// ```
361    pub fn swap_from_guard(
362        &self,
363        new_value: StoreGuard<'domain, T, DOMAIN_ID>,
364    ) -> StoreGuard<'domain, T, DOMAIN_ID> {
365        assert!(
366            core::ptr::eq(new_value.domain, self.domain),
367            "Cannot use guarded value from different domain"
368        );
369
370        let new_ptr = new_value.ptr;
371        core::mem::forget(new_value);
372        let old_ptr = self.ptr.swap(new_ptr as *mut T, Ordering::AcqRel);
373        StoreGuard {
374            ptr: old_ptr,
375            domain: self.domain,
376        }
377    }
378
379    /// Stores a value into the `AtomBox` if its current value equals `current_value`.
380    ///
381    /// The return value is a result indicating whether the new value was written.
382    /// On success, this value is guaranteed to be equal to `current_value` and the return value is
383    /// a StoreGuard which dereferences to the old value.
384    /// On failure, the `Err` contains a LoadGaurd which dereferences to the `current_value`.
385    ///
386    /// **Note:** This method is only available on platforms that support atomic operations on
387    /// pointers.
388    ///
389    /// # Example
390    /// ```
391    /// use atom_box::AtomBox;
392    ///
393    /// let atom_box = AtomBox::new(0);
394    /// let mut current_value = atom_box.load();
395    /// let initial_value = *current_value;
396    /// let _ = loop {
397    ///     let new_value = *current_value + 1;
398    ///     match atom_box.compare_exchange(current_value, new_value) {
399    ///         Ok(value) => {
400    ///             break value;
401    ///         }
402    ///         Err(value) => {
403    ///             current_value = value;
404    ///         }
405    ///     }
406    /// };
407    /// let new_value = atom_box.load();
408    /// assert!(
409    ///     *new_value > initial_value,
410    ///     "Value should have been increased"
411    /// );
412    /// ```
413    pub fn compare_exchange(
414        &self,
415        current_value: LoadGuard<'domain, T, DOMAIN_ID>,
416        new_value: T,
417    ) -> Result<StoreGuard<'domain, T, DOMAIN_ID>, LoadGuard<'domain, T, DOMAIN_ID>> {
418        let new_ptr = Box::into_raw(Box::new(new_value));
419        match self.ptr.compare_exchange(
420            current_value.ptr as *mut T,
421            new_ptr,
422            Ordering::AcqRel,
423            Ordering::Acquire,
424        ) {
425            Ok(ptr) => Ok(StoreGuard {
426                ptr,
427                domain: self.domain,
428            }),
429            Err(ptr) => Err(LoadGuard {
430                ptr,
431                domain: self.domain,
432                haz_ptr: None,
433            }),
434        }
435    }
436
437    /// Stores a value into the `AtomBox` if its current value equals `current_value`.
438    ///
439    /// The return value is a result indicating whether the new value was written.
440    /// On success, this value is guaranteed to be equal to `current_value` and the return value is
441    /// a StoreGuard which dereferences to the old value.
442    /// On failure, the `Err` contains a LoadGaurd which dereferences to the `current_value`.
443    ///
444    /// **Note:** This method is only available on platforms that support atomic operations on
445    /// pointers.
446    ///
447    /// # Panics
448    ///
449    /// Panics if the guard is associated with a different domain.
450    ///
451    /// # example
452    /// ```
453    /// use atom_box::AtomBox;
454    ///
455    /// let atom_box1 = AtomBox::new(0);
456    /// let atom_box2 = AtomBox::new(1);
457    ///
458    /// let mut guard = atom_box2.swap(2);
459    /// let mut current_value = atom_box1.load();
460    /// let _ = loop {
461    ///     match atom_box1.compare_exchange_from_guard(current_value, guard) {
462    ///         Ok(value) => {
463    ///             break value;
464    ///         }
465    ///         Err((value, returned_guard)) => {
466    ///             current_value = value;
467    ///             guard = returned_guard;
468    ///         }
469    ///     }
470    /// };
471    /// let new_value = atom_box1.load();
472    /// assert!(
473    ///     *new_value == 1,
474    ///     "value should have been increased"
475    /// );
476    /// ```
477    ///
478    /// The following example will fail to compile.
479    ///
480    /// ```compile_fail
481    /// use atom_box::{AtomBox, domain::{Domain, Reclaimstrategy}};
482    ///
483    /// const custom_domain_id: usize = 42;
484    /// static custom_domain: domain<custom_domain_id> = domain::new(reclaimstrategy::eager);
485    ///
486    /// let atom_box1 = AtomBox::new_with_domain("hello", &custom_domain);
487    /// let atom_box2 = AtomBox::new("world");
488    ///
489    /// let guard = atom_box1.swap("bye bye");
490    /// let current_value = atom_box2.load();
491    /// let _ = atom_box2.compare_exchange_from_guard(current_value, guard);
492    /// ```
493    pub fn compare_exchange_from_guard(
494        &self,
495        current_value: LoadGuard<'domain, T, DOMAIN_ID>,
496        new_value: StoreGuard<'domain, T, DOMAIN_ID>,
497    ) -> Result<
498        StoreGuard<'domain, T, DOMAIN_ID>,
499        (
500            LoadGuard<'domain, T, DOMAIN_ID>,
501            StoreGuard<'domain, T, DOMAIN_ID>,
502        ),
503    > {
504        assert!(
505            core::ptr::eq(new_value.domain, self.domain),
506            "Cannot use guarded value from different domain"
507        );
508
509        let new_ptr = new_value.ptr;
510        match self.ptr.compare_exchange(
511            current_value.ptr as *mut T,
512            new_ptr as *mut T,
513            Ordering::AcqRel,
514            Ordering::Acquire,
515        ) {
516            Ok(ptr) => {
517                core::mem::forget(new_value);
518                Ok(StoreGuard {
519                    ptr,
520                    domain: self.domain,
521                })
522            }
523            Err(ptr) => Err((
524                LoadGuard {
525                    ptr,
526                    domain: self.domain,
527                    haz_ptr: None,
528                },
529                new_value,
530            )),
531        }
532    }
533
534    /// Stores a value into the `AtomBox` if the current value is the same as the `current` value.
535    ///
536    /// Unlike [`AtomBox::compare_exchange`], this function is allowed to spuriously fail even when the
537    /// comparison succeeds, which can result in more efficient code on some platforms. The
538    /// return value is a result indicating whether the new value was written and containing the
539    /// previous value.
540    ///
541    /// **Note:** This method is only available on platforms that support atomic operations on
542    /// pointers.
543    ///
544    /// # Example
545    /// ```
546    /// use atom_box::AtomBox;
547    ///
548    /// let atom_box = AtomBox::new(0);
549    /// let mut current_value = atom_box.load();
550    /// let initial_value = *current_value;
551    /// let _ = loop {
552    ///     let new_value = *current_value + 1;
553    ///     match atom_box.compare_exchange_weak(current_value, new_value) {
554    ///         Ok(value) => {
555    ///             break value;
556    ///         }
557    ///         Err(value) => {
558    ///             current_value = value;
559    ///         }
560    ///     }
561    /// };
562    /// let new_value = atom_box.load();
563    /// assert!(
564    ///     *new_value > initial_value,
565    ///     "Value should have been increased"
566    /// );
567    /// ```
568    pub fn compare_exchange_weak(
569        &self,
570        current_value: LoadGuard<'domain, T, DOMAIN_ID>,
571        new_value: T,
572    ) -> Result<StoreGuard<'domain, T, DOMAIN_ID>, LoadGuard<'domain, T, DOMAIN_ID>> {
573        let new_ptr = Box::into_raw(Box::new(new_value));
574        match self.ptr.compare_exchange_weak(
575            current_value.ptr as *mut T,
576            new_ptr,
577            Ordering::AcqRel,
578            Ordering::Acquire,
579        ) {
580            Ok(ptr) => Ok(StoreGuard {
581                ptr,
582                domain: self.domain,
583            }),
584            Err(ptr) => Err(LoadGuard {
585                ptr,
586                domain: self.domain,
587                haz_ptr: None,
588            }),
589        }
590    }
591
592    /// Stores a value into the `AtomBox` if the current value is the same as the `current` value.
593    ///
594    /// Unlike [`AtomBox::compare_exchange_from_guard`], this function is allowed to spuriously fail even when the
595    /// comparison succeeds, which can result in more efficient code on some platforms. The
596    /// return value is a result indicating whether the new value was written and containing the
597    /// previous value.
598    ///
599    /// **Note:** This method is only available on platforms that support atomic operations on
600    /// pointers.
601    ///
602    /// # Panics
603    ///
604    /// Panics if the guard is associated with a different domain.
605    ///
606    /// # example
607    /// ```
608    /// use atom_box::AtomBox;
609    ///
610    /// let atom_box1 = AtomBox::new(0);
611    /// let atom_box2 = AtomBox::new(1);
612    ///
613    /// let mut guard = atom_box2.swap(2);
614    /// let mut current_value = atom_box1.load();
615    /// let _ = loop {
616    ///     match atom_box1.compare_exchange_weak_from_guard(current_value, guard) {
617    ///         Ok(value) => {
618    ///             break value;
619    ///         }
620    ///         Err((value, returned_guard)) => {
621    ///             current_value = value;
622    ///             guard = returned_guard;
623    ///         }
624    ///     }
625    /// };
626    /// let new_value = atom_box1.load();
627    /// assert!(
628    ///     *new_value == 1,
629    ///     "value should have been increased"
630    /// );
631    /// ```
632    ///
633    /// The following example will fail to compile.
634    ///
635    /// ```compile_fail
636    /// use atom_box::{AtomBox, domain::{Domain, ReclaimStrategy}};
637    ///
638    /// const custom_domain_id: usize = 42;
639    /// static custom_domain: Domain<custom_domain_id> = Domain::new(ReclaimStrategy::Eager);
640    ///
641    /// let atom_box1 = AtomBox::new_with_domain("hello", &custom_domain);
642    /// let atom_box2 = AtomBox::new("world");
643    ///
644    /// let guard = atom_box1.swap("bye bye");
645    /// let current_value = atom_box2.load();
646    /// let _ = atom_box2.compare_exchange_weak_from_guard(current_value, guard);
647    /// ```
648    pub fn compare_exchange_weak_from_guard(
649        &self,
650        current_value: LoadGuard<'domain, T, DOMAIN_ID>,
651        new_value: StoreGuard<'domain, T, DOMAIN_ID>,
652    ) -> Result<
653        StoreGuard<'domain, T, DOMAIN_ID>,
654        (
655            LoadGuard<'domain, T, DOMAIN_ID>,
656            StoreGuard<'domain, T, DOMAIN_ID>,
657        ),
658    > {
659        assert!(
660            core::ptr::eq(new_value.domain, self.domain),
661            "Cannot use guarded value from different domain"
662        );
663
664        let new_ptr = new_value.ptr;
665        match self.ptr.compare_exchange_weak(
666            current_value.ptr as *mut T,
667            new_ptr as *mut T,
668            Ordering::AcqRel,
669            Ordering::Acquire,
670        ) {
671            Ok(ptr) => {
672                core::mem::forget(new_value);
673                Ok(StoreGuard {
674                    ptr,
675                    domain: self.domain,
676                })
677            }
678            Err(ptr) => Err((
679                LoadGuard {
680                    ptr,
681                    domain: self.domain,
682                    haz_ptr: None,
683                },
684                new_value,
685            )),
686        }
687    }
688}
689
690impl<'domain, T, const DOMAIN_ID: usize> Drop for AtomBox<'domain, T, DOMAIN_ID> {
691    fn drop(&mut self) {
692        // # Safety
693        //
694        // The pointer to this object was originally created via box into raw.
695        // The heap allocated value cannot be dropped via external code.
696        // We are the only person with this pointer since we have an exclusive reference to the box
697        // and we would not have this pointer if we had given it out in a StoreGuard. There might
698        // be other people referencing it as a read only value where it is protected
699        // via hazard pointers.
700        // We are safe to flag it for retire, where it will be reclaimed when it is no longer
701        // protected by any hazard pointers.
702        let ptr = self.ptr.load(Ordering::Relaxed);
703        unsafe { self.domain.retire(ptr) };
704    }
705}
706
707/// Contains a reference to a value that was previously contained in an `AtomBox`.
708///
709/// Returned from the store methods method on `AtomBox`. This value can be passed to the
710/// `from_guard` methods to store this value in an `AtomBox` associated with the same domain.
711///
712/// Dereferences to the value.
713pub struct StoreGuard<'domain, T, const DOMAIN_ID: usize> {
714    ptr: *const T,
715    domain: &'domain Domain<DOMAIN_ID>,
716}
717
718impl<T, const DOMAIN_ID: usize> Deref for StoreGuard<'_, T, DOMAIN_ID> {
719    type Target = T;
720    fn deref(&self) -> &Self::Target {
721        // # Safety
722        //
723        // The pointer is protected by the hazard pointer so will not have been dropped
724        // The pointer was created via a Box so is aligned and there are no mutable references
725        // since we do not give any out.
726        unsafe { self.ptr.as_ref().expect("Non null") }
727    }
728}
729
730impl<T, const DOMAIN_ID: usize> Drop for StoreGuard<'_, T, DOMAIN_ID> {
731    fn drop(&mut self) {
732        // # Safety
733        //
734        // The pointer to this object was originally created via box into raw.
735        // The heap allocated value cannot be dropped via external code.
736        // We are the only person with this pointer in a store guard. There might
737        // be other people referencing it as a read only value where it is protected
738        // via hazard pointers.
739        // We are safe to flag it for retire, where it will be reclaimed when it is no longer
740        // protected by any hazard pointers.
741        unsafe { self.domain.retire(self.ptr as *mut T) };
742    }
743}
744
745/// Contains a reference to a value that was stored in a `AtomBox`.
746///
747/// Returned as the result of calling [`AtomBox::load`].
748///
749/// The value is guaranteed not to be dropped before this guard is dropped.
750///
751/// Dereferences to the value.
752pub struct LoadGuard<'domain, T, const DOMAIN_ID: usize> {
753    ptr: *const T,
754    domain: &'domain Domain<DOMAIN_ID>,
755    haz_ptr: Option<HazardPointer<'domain>>,
756}
757
758impl<T, const DOMAIN_ID: usize> Drop for LoadGuard<'_, T, DOMAIN_ID> {
759    fn drop(&mut self) {
760        if let Some(haz_ptr) = self.haz_ptr.take() {
761            self.domain.release_hazard_ptr(haz_ptr);
762        }
763    }
764}
765
766impl<T, const DOMAIN_ID: usize> Deref for LoadGuard<'_, T, DOMAIN_ID> {
767    type Target = T;
768    fn deref(&self) -> &Self::Target {
769        // # Safety
770        //
771        // The pointer is protected by the hazard pointer so will not have been dropped
772        // The pointer was created via a Box so is aligned and there are no mutable references
773        // since we do not give any out.
774        unsafe { self.ptr.as_ref().expect("Non null") }
775    }
776}
777
778#[cfg(not(loom))]
779#[cfg(test)]
780mod test {
781    use super::*;
782
783    pub(crate) use core::sync::atomic::AtomicUsize;
784
785    static TEST_DOMAIN: domain::Domain<1> = Domain::new(domain::ReclaimStrategy::Eager);
786
787    struct DropTester<'a, T> {
788        drop_count: &'a AtomicUsize,
789        value: T,
790    }
791
792    impl<'a, T> Drop for DropTester<'a, T> {
793        fn drop(&mut self) {
794            self.drop_count.fetch_add(1, Ordering::AcqRel);
795        }
796    }
797
798    impl<'a, T> Deref for DropTester<'a, T> {
799        type Target = T;
800        fn deref(&self) -> &Self::Target {
801            &self.value
802        }
803    }
804
805    #[test]
806    fn single_thread_retire() {
807        let atom_box = AtomBox::new(20);
808
809        let value = atom_box.load();
810        assert_eq!(
811            *value, 20,
812            "The correct values is returned when dereferencing"
813        );
814        assert_eq!(
815            value.ptr,
816            value.haz_ptr.as_ref().unwrap().0.load(Ordering::Acquire),
817            "The hazard pointer is protecting the correct pointer"
818        );
819
820        {
821            // Immediately retire the original value
822            let guard = atom_box.swap(30);
823            assert_eq!(
824                guard.ptr, value.ptr,
825                "The guard returned after swap contains a pointer to the old value"
826            );
827            let new_value = atom_box.load();
828            assert_eq!(*new_value, 30, "The new value has been set correctly");
829        }
830        assert_eq!(
831            *value, 20,
832            "We are still able to access the old value as a result of the original load"
833        );
834        drop(value);
835        let _ = atom_box.swap(40);
836        let final_value = atom_box.load();
837        assert_eq!(
838            *final_value, 40,
839            "When we load again we get a handle to the latest value"
840        );
841    }
842
843    #[test]
844    fn drop_test() {
845        let drop_count = AtomicUsize::new(0);
846        let value = DropTester {
847            drop_count: &drop_count,
848            value: 20,
849        };
850        let atom_box = AtomBox::new_with_domain(value, &TEST_DOMAIN);
851
852        let value = atom_box.load();
853        assert_eq!(
854            drop_count.load(Ordering::Acquire),
855            0,
856            "No values have been dropped yet"
857        );
858        assert_eq!(**value, 20, "The correct value is returned via load");
859        assert_eq!(
860            value.ptr as *mut usize,
861            value.haz_ptr.as_ref().unwrap().0.load(Ordering::Acquire),
862            "The value is protected by the hazard pointer"
863        );
864
865        {
866            // Immediately retire the original value
867            let guard = atom_box.swap(DropTester {
868                drop_count: &drop_count,
869                value: 30,
870            });
871            assert_eq!(guard.ptr, value.ptr, "When we swap the value we get back a guard that contains a pointer to the old value");
872            let new_value = atom_box.load();
873            assert_eq!(
874                **new_value, 30,
875                "When we dereference the load, we get back a reference to the new value"
876            );
877            drop(guard);
878        }
879        assert_eq!(
880            drop_count.load(Ordering::SeqCst),
881            0,
882            "Value should not be dropped while there is an active reference to it"
883        );
884        assert_eq!(**value, 20, "We are still able to access the original value since we have been holding a load guard");
885        drop(value);
886        let _ = atom_box.swap(DropTester {
887            drop_count: &drop_count,
888            value: 40,
889        });
890        let final_value = atom_box.load();
891        assert_eq!(**final_value, 40, "The value has been updated");
892        assert_eq!(
893            drop_count.load(Ordering::SeqCst),
894            2,
895            "Both of the old values should now be dropped"
896        );
897    }
898
899    #[test]
900    fn swap_from_gaurd_test() {
901        let drop_count = AtomicUsize::new(0);
902        let drop_count_for_placeholder = AtomicUsize::new(0);
903        let value1 = DropTester {
904            drop_count: &drop_count,
905            value: 10,
906        };
907        let value2 = DropTester {
908            drop_count: &drop_count,
909            value: 20,
910        };
911        let atom_box1 = AtomBox::new_with_domain(value1, &TEST_DOMAIN);
912        let atom_box2 = AtomBox::new_with_domain(value2, &TEST_DOMAIN);
913
914        {
915            // Immediately retire the original value
916            let guard1 = atom_box1.swap(DropTester {
917                drop_count: &drop_count_for_placeholder,
918                value: 30,
919            });
920            let guard2 = atom_box2.swap_from_guard(guard1);
921            let _ = atom_box1.swap_from_guard(guard2);
922            let new_value1 = atom_box1.load();
923            let new_value2 = atom_box2.load();
924            assert_eq!(
925                **new_value1, 20,
926                "The values in the boxes should have been swapped"
927            );
928            assert_eq!(
929                **new_value2, 10,
930                "The values in the boxes should have been swapped"
931            );
932        }
933        assert_eq!(
934            drop_count_for_placeholder.load(Ordering::Acquire),
935            1,
936            "The placeholder value should have been dropped"
937        );
938        assert_eq!(
939            drop_count.load(Ordering::Acquire),
940            0,
941            "Neither of the initial values should have been dropped"
942        );
943    }
944}