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}