ptr_cell/
lib.rs

1//! # Simple thread-safe cell
2//!
3//! [`PtrCell`] is an atomic cell type that allows safe, concurrent access to shared data. No
4//! [`std`][1], no data races, no [nasal demons][2] (undefined behavior), and most importantly, no
5//! locks
6//!
7//! This type is only useful in scenarios where you need to update a shared value by moving in and
8//! out of it. If you want to concurrently update a value through mutable references and don't
9//! require support for `no_std`, take a look at the standard [`Mutex`][3] and [`RwLock`][4] instead
10//!
11//! #### Offers:
12//!
13//! - **Familiarity**: `PtrCell`'s API was modelled after `std`'s [Cell](core::cell::Cell)
14//!
15//! - **Easy Concurrency**: No more `Arc<Mutex<T>>`, `Arc::clone()`, and `Mutex::lock().expect()`!
16//! Leave the data static and then point to it when you need to. It's a _single instruction_ on most
17//! modern platforms
18//!
19//! #### Limitations:
20//!
21//! - **Heap Allocation**: Every value you insert into `PtrCell` must first be allocated using
22//! [`Box`]. Allocating on the heap is, computationally, a moderately expensive operation. To
23//! address this, the cell exposes a pointer API that can be used to avoid allocating the same
24//! values multiple times. Future releases will primarily rely on the stack
25//!
26//! ## Usage
27//!
28//! ```rust
29//! use ptr_cell::{PtrCell, Semantics::Relaxed};
30//!
31//! let cell: PtrCell<u16> = 0x81D.into();
32//!
33//! assert_eq!(cell.replace(Some(2047), Relaxed), Some(0x81D));
34//! assert_eq!(cell.is_empty(Relaxed), false);
35//! assert_eq!(cell.take(Relaxed), Some(2047))
36//! ```
37//!
38//! ## Semantics
39//!
40//! [`PtrCell`] allows you to specify memory ordering semantics for its internal atomic operations
41//! through the [`Semantics`] enum. Each variant is different in how it balances synchronization and
42//! performace. Here's a comparison of the available semantics:
43//!
44//! | Variant | Overhead | Synchronization |
45//! |---|---|---|
46//! | [`Relaxed`](Semantics::Relaxed) | Negligible | None |
47//! | [`Coupled`](Semantics::Coupled) | Acceptable | Intuitive |
48//! | [`Ordered`](Semantics::Ordered) | Noticeable | Strict |
49//!
50//! `Coupled` is what you'd typically use. However, other orderings have their use cases too. For
51//! example, the `Relaxed` semantics could be useful when the operations are already synchronized
52//! through other means, like [fences](core::sync::atomic::fence). As always, the documentation for
53//! each item contains more details
54//!
55//! ## Examples
56//!
57//! The code below finds the maximum value of a sequence by concurrently processing its halves.
58//! Notice how the code doesn't read the shared value. Instead, it uses moves and corrects previous
59//! operations as new data comes in
60//!
61//! ```rust
62//! use ptr_cell::{PtrCell, Semantics};
63//! use std::sync::Arc;
64//!
65//! fn main() {
66//!     const VALUES: [u8; 11] = [47, 12, 88, 45, 67, 34, 78, 90, 11, 77, 33];
67//!
68//!     let cell = PtrCell::default();
69//!     let maximum = Arc::new(cell);
70//!
71//!     let (left, right) = VALUES.split_at(VALUES.len() / 2);
72//!
73//!     let handles = [left, right].map(|half| {
74//!         let maximum = Arc::clone(&maximum);
75//!
76//!         std::thread::spawn(move || maximize_in(half, &maximum))
77//!     });
78//!
79//!     for worker in handles {
80//!         if let Err(payload) = worker.join() {
81//!             std::panic::resume_unwind(payload)
82//!         }
83//!     }
84//!
85//!     assert_eq!(maximum.take(Semantics::Relaxed), Some(90))
86//! }
87//!
88//! fn maximize_in<T>(sequence: &[T], buffer: &PtrCell<T>)
89//! where
90//!     T: Ord + Copy,
91//! {
92//!     for &item in sequence {
93//!         let mut slot = Some(item);
94//!
95//!         loop {
96//!             let previous = buffer.replace(slot, Semantics::Relaxed);
97//!
98//!             match slot < previous {
99//!                 true => slot = previous,
100//!                 false => break,
101//!             }
102//!         }
103//!     }
104//! }
105//! ```
106//!
107//! [1]: https://doc.rust-lang.org/std/index.html
108//! [2]: https://groups.google.com/g/comp.std.c/c/ycpVKxTZkgw/m/S2hHdTbv4d8J?hl=en
109//! [3]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
110//! [4]: https://doc.rust-lang.org/std/sync/struct.RwLock.html
111
112#![no_std]
113#![warn(missing_docs, clippy::all, clippy::pedantic, clippy::cargo)]
114#![allow(clippy::must_use_candidate)]
115#![forbid(unsafe_op_in_unsafe_fn)]
116
117extern crate alloc;
118
119use alloc::boxed::Box;
120use core::sync::atomic::Ordering;
121
122// 3.0.0:
123// - Just fix `replace_ptr` already!!! \
124// - Make `Semantics` exhaustive       |
125// - Add the default `std` feature     /
126// - Figure out how to properly generalize to the stack (see notes below)
127// - Implement `get`, `update`, and some traits by using brief spinlocking
128// - Add "virtually" to "no locks" in the top-level docs (very important)
129// - Add `from_mut` like on std's Cell
130
131// It's possible to ditch heap allocation entirely if we pre-allocate a buffer of type T.
132// Pre-allocating an array of N buffers (const N: usize) could amortize performance losses during
133// periods of high contention
134
135// Top-level:
136//
137// ## Features
138//
139// - **`std`**: Enables everything that may depend on the standard library. Currently, there are no
140// such items. Could optimize performace in future updates
141
142/// Thread-safe cell based on atomic pointers
143///
144/// This type stores its data externally by _leaking_ it with [`Box`]. Synchronization is achieved
145/// by atomically manipulating pointers to the data
146///
147/// # Usage
148///
149/// ```rust
150/// use ptr_cell::{PtrCell, Semantics::Relaxed};
151///
152/// let cell: PtrCell<u16> = 0x81D.into();
153///
154/// assert_eq!(cell.replace(Some(2047), Relaxed), Some(0x81D));
155/// assert_eq!(cell.is_empty(Relaxed), false);
156/// assert_eq!(cell.take(Relaxed), Some(2047))
157/// ```
158///
159/// # Pointer Safety
160///
161/// When dereferencing a pointer to the cell's value, you must ensure that the pointed-to memory
162/// hasn't been [reclaimed](Self::heap_reclaim). For example, [`replace`](Self::replace) and its
163/// derivatives ([`set`](Self::set) and [`take`](Self::take)) automatically reclaim memory. Be
164/// careful not to miss any calls to such functions made from other threads
165///
166/// This also applies to externally-sourced pointers, like the `ptr` parameter in
167/// [`from_ptr`](Self::from_ptr)
168#[repr(transparent)]
169pub struct PtrCell<T> {
170    /// Pointer to the contained value
171    ///
172    /// #### Invariants
173    ///
174    /// - **If non-null**: Must point to memory that conforms to the [memory layout][1] used by
175    ///   [`Box`]
176    ///
177    /// [1]: https://doc.rust-lang.org/std/boxed/index.html#memory-layout
178    value: core::sync::atomic::AtomicPtr<T>,
179}
180
181impl<T> PtrCell<T> {
182    /// Inserts the value constructed from this cell by `new` into the cell itself
183    ///
184    /// Think of this like the `push` method of a linked list, where each node contains a `PtrCell`
185    ///
186    /// # Examples
187    ///
188    /// The code below turns a sentence into a naive linked list of words, which is then assembled
189    /// back into a [`String`][1]
190    ///
191    /// ```rust
192    /// use ptr_cell::{PtrCell, Semantics};
193    ///
194    /// struct Node<T> {
195    ///     pub value: T,
196    ///     pub next: PtrCell<Self>,
197    /// }
198    ///
199    /// impl<T> AsMut<PtrCell<Self>> for Node<T> {
200    ///     fn as_mut(&mut self) -> &mut ptr_cell::PtrCell<Self> {
201    ///         &mut self.next
202    ///     }
203    /// }
204    ///
205    /// let cell = PtrCell::default();
206    ///
207    /// for value in "Hachó en México".split_whitespace().rev() {
208    ///     cell.map_owner(|next| Node { value, next }, Semantics::Relaxed);
209    /// }
210    ///
211    /// let Node { value, mut next } = cell
212    ///     .take(Semantics::Relaxed)
213    ///     .expect("Some values should've been inserted into the cell");
214    ///
215    /// let mut decoded = value.to_string();
216    /// while let Some(node) = next.take(Semantics::Relaxed) {
217    ///     decoded.extend([" ", node.value]);
218    ///     next = node.next
219    /// }
220    ///
221    /// assert_eq!(decoded, "Hachó en México")
222    /// ```
223    ///
224    /// [1]: https://doc.rust-lang.org/std/string/struct.String.html
225    pub fn map_owner<F>(&self, new: F, order: Semantics)
226    where
227        F: FnOnce(Self) -> T,
228        T: AsMut<Self>,
229    {
230        let value_ptr = self.get_ptr(order);
231        let value = unsafe { Self::from_ptr(value_ptr) };
232
233        let owner_slot = Some(new(value));
234        let owner_ptr = Self::heap_leak(owner_slot);
235
236        let owner = unsafe { &mut *owner_ptr };
237        let value_ptr = owner.as_mut().value.get_mut();
238
239        loop {
240            let value_ptr_result = self.value.compare_exchange_weak(
241                *value_ptr,
242                owner_ptr,
243                order.read_write(),
244                order.read(),
245            );
246
247            let Err(modified) = value_ptr_result else {
248                break;
249            };
250
251            *value_ptr = modified;
252            core::hint::spin_loop();
253        }
254    }
255
256    /// Swaps the values of two cells
257    ///
258    /// # Usage
259    ///
260    /// ```rust
261    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
262    ///
263    /// let one: PtrCell<u8> = 1.into();
264    /// let mut two: PtrCell<u8> = 2.into();
265    ///
266    /// one.swap(&mut two, Relaxed);
267    ///
268    /// assert_eq!(two.take(Relaxed), Some(1));
269    /// assert_eq!(one.take(Relaxed), Some(2))
270    /// ```
271    #[inline]
272    pub fn swap(&self, other: &mut Self, order: Semantics) {
273        let other_ptr = other.get_ptr(Semantics::Relaxed);
274
275        unsafe {
276            let ptr = self.replace_ptr(other_ptr, order);
277            other.set_ptr(ptr, Semantics::Relaxed);
278        }
279    }
280
281    /// Takes out the cell's value
282    ///
283    /// # Usage
284    ///
285    /// ```rust
286    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
287    ///
288    /// let cell: PtrCell<u8> = 45.into();
289    ///
290    /// assert_eq!(cell.take(Relaxed), Some(45));
291    /// assert_eq!(cell.take(Relaxed), None)
292    /// ```
293    #[inline]
294    pub fn take(&self, order: Semantics) -> Option<T> {
295        self.replace(None, order)
296    }
297
298    /// Takes out the cell's pointer
299    ///
300    /// # Safety
301    ///
302    /// Not inherently unsafe. See [Pointer Safety][1]
303    ///
304    /// # Usage
305    ///
306    /// ```rust
307    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
308    ///
309    /// let cell: PtrCell<u8> = 45.into();
310    /// let ptr = cell.take_ptr(Relaxed);
311    ///
312    /// assert_eq!(unsafe { ptr_cell::PtrCell::heap_reclaim(ptr) }, Some(45));
313    /// assert_eq!(cell.take_ptr(Relaxed), std::ptr::null_mut())
314    /// ```
315    ///
316    /// [1]: https://docs.rs/ptr_cell/latest/ptr_cell/struct.PtrCell.html#pointer-safety
317    #[inline]
318    pub fn take_ptr(&self, order: Semantics) -> *mut T {
319        self.replace_ptr(core::ptr::null_mut(), order)
320    }
321
322    /// Inserts a value into the cell
323    ///
324    /// # Usage
325    ///
326    /// ```rust
327    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
328    ///
329    /// let cell = PtrCell::default();
330    /// cell.set(Some(1776), Relaxed);
331    ///
332    /// assert_eq!(cell.take(Relaxed), Some(1776))
333    /// ```
334    #[inline]
335    pub fn set(&self, slot: Option<T>, order: Semantics) {
336        let _ = self.replace(slot, order);
337    }
338
339    /// Inserts a pointer into the cell
340    ///
341    /// # Safety
342    ///
343    /// The pointed-to memory must conform to the [memory layout][1] used by [`Box`]
344    ///
345    /// # Usage
346    ///
347    /// ```rust
348    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
349    ///
350    /// let cell = PtrCell::default();
351    ///
352    /// let ptr = PtrCell::heap_leak(Some(1776));
353    /// unsafe { cell.set_ptr(ptr, Relaxed) };
354    ///
355    /// assert_eq!(cell.take(Relaxed), Some(1776))
356    /// ```
357    ///
358    /// [1]: https://doc.rust-lang.org/std/boxed/index.html#memory-layout
359    #[inline]
360    pub unsafe fn set_ptr(&self, ptr: *mut T, order: Semantics) {
361        self.value.store(ptr, order.write());
362    }
363
364    /// Replaces the cell's value
365    ///
366    /// # Usage
367    ///
368    /// ```rust
369    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
370    ///
371    /// let cell = PtrCell::from('a');
372    ///
373    /// assert_eq!(cell.replace(Some('b'), Relaxed), Some('a'));
374    /// assert_eq!(cell.take(Relaxed), Some('b'))
375    /// ```
376    #[inline]
377    #[must_use = "use `.set()` if you don't need the old value"]
378    pub fn replace(&self, slot: Option<T>, order: Semantics) -> Option<T> {
379        let new_leak = Self::heap_leak(slot);
380
381        unsafe {
382            let old_leak = self.replace_ptr(new_leak, order);
383            Self::heap_reclaim(old_leak)
384        }
385    }
386
387    /// Replaces the cell's pointer
388    ///
389    /// **WARNING: THIS FUNCTION WAS ERRONEOUSLY LEFT SAFE. IT'S UNSAFE AND WILL BE MARKED AS SUCH
390    /// IN THE NEXT MAJOR RELEASE**
391    ///
392    /// # Safety
393    ///
394    /// The pointed-to memory must conform to the [memory layout][1] used by [`Box`]
395    ///
396    /// See also: [Pointer Safety][2]
397    ///
398    /// # Usage
399    ///
400    /// ```rust
401    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
402    ///
403    /// unsafe {
404    ///     let a = PtrCell::heap_leak(Some('a'));
405    ///     let b = PtrCell::heap_leak(Some('b'));
406    ///
407    ///     let cell = PtrCell::from_ptr(a);
408    ///
409    ///     assert_eq!(cell.replace_ptr(b, Relaxed), a);
410    ///     assert_eq!(cell.take_ptr(Relaxed), b);
411    ///
412    ///     PtrCell::heap_reclaim(a);
413    ///     PtrCell::heap_reclaim(b);
414    /// }
415    /// ```
416    ///
417    /// [1]: https://doc.rust-lang.org/std/boxed/index.html#memory-layout
418    /// [2]: https://docs.rs/ptr_cell/latest/ptr_cell/struct.PtrCell.html#pointer-safety
419    #[inline]
420    #[must_use = "use `.set_ptr()` if you don't need the old pointer"]
421    pub fn replace_ptr(&self, ptr: *mut T, order: Semantics) -> *mut T {
422        self.value.swap(ptr, order.read_write())
423    }
424
425    /// Mutably borrows the cell's value
426    ///
427    /// # Usage
428    ///
429    /// ```rust
430    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
431    ///
432    /// let mut text = PtrCell::from("Point".to_string());
433    /// text.get_mut()
434    ///     .expect("The cell should contain a value")
435    ///     .push_str("er");
436    ///
437    /// assert_eq!(text.take(Relaxed), Some("Pointer".to_string()))
438    /// ```
439    #[inline]
440    pub fn get_mut(&mut self) -> Option<&mut T> {
441        let leak = *self.value.get_mut();
442
443        non_null(leak).map(|ptr| unsafe { &mut *ptr })
444    }
445
446    /// Returns a pointer to the cell's value
447    ///
448    /// # Safety
449    ///
450    /// Not inherently unsafe. See [Pointer Safety][1]
451    ///
452    /// # Usage
453    ///
454    /// ```rust
455    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
456    ///
457    /// let cell = PtrCell::<[u8; 3]>::default();
458    ///
459    /// assert_eq!(cell.get_ptr(Relaxed), std::ptr::null_mut())
460    /// ```
461    ///
462    /// [1]: https://docs.rs/ptr_cell/latest/ptr_cell/struct.PtrCell.html#pointer-safety
463    #[inline]
464    pub fn get_ptr(&self, order: Semantics) -> *mut T {
465        self.value.load(order.read())
466    }
467
468    /// Determines whether this cell is empty
469    ///
470    /// # Usage
471    ///
472    /// ```rust
473    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
474    ///
475    /// let cell = PtrCell::<[u8; 3]>::default();
476    ///
477    /// assert!(cell.is_empty(Relaxed))
478    /// ```
479    #[inline]
480    pub fn is_empty(&self, order: Semantics) -> bool {
481        self.get_ptr(order).is_null()
482    }
483
484    /// Constructs a cell
485    ///
486    /// # Usage
487    ///
488    /// ```rust
489    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
490    ///
491    /// let cell = PtrCell::new(Some(0xFAA));
492    ///
493    /// assert_eq!(cell.take(Relaxed), Some(0xFAA));
494    /// assert!(cell.is_empty(Relaxed))
495    /// ```
496    #[inline]
497    #[must_use]
498    pub fn new(slot: Option<T>) -> Self {
499        let ptr = Self::heap_leak(slot);
500
501        unsafe { Self::from_ptr(ptr) }
502    }
503
504    /// Constructs a cell that owns [leaked](Self::heap_leak) memory
505    ///
506    /// A null pointer represents [`None`]
507    ///
508    /// # Safety
509    ///
510    /// The memory must conform to the [memory layout][1] used by [`Box`]
511    ///
512    /// # Usage
513    ///
514    /// ```rust
515    /// use ptr_cell::{PtrCell, Semantics::Relaxed};
516    ///
517    /// let ptr = PtrCell::heap_leak(Some(0xFAA));
518    /// let cell = unsafe { PtrCell::from_ptr(ptr) };
519    ///
520    /// assert_eq!(cell.take(Relaxed), Some(0xFAA));
521    /// assert!(cell.is_empty(Relaxed))
522    /// ```
523    ///
524    /// [1]: https://doc.rust-lang.org/std/boxed/index.html#memory-layout
525    #[inline]
526    pub const unsafe fn from_ptr(ptr: *mut T) -> Self {
527        let value = core::sync::atomic::AtomicPtr::new(ptr);
528
529        Self { value }
530    }
531
532    /// Reclaims ownership of [leaked](Self::heap_leak) memory
533    ///
534    /// A null pointer represents [`None`]
535    ///
536    /// # Safety
537    ///
538    /// The memory must conform to the [memory layout][1] used by [`Box`]
539    ///
540    /// Dereferencing `ptr` after this function has been called may cause undefined behavior
541    ///
542    /// # Usage
543    ///
544    /// ```rust
545    /// use ptr_cell::PtrCell;
546    ///
547    /// let ptr = PtrCell::heap_leak(Some(1155));
548    ///
549    /// assert_eq!(unsafe { PtrCell::heap_reclaim(ptr) }, Some(1155))
550    /// ```
551    ///
552    /// [1]: https://doc.rust-lang.org/std/boxed/index.html#memory-layout
553    #[inline]
554    pub unsafe fn heap_reclaim(ptr: *mut T) -> Option<T> {
555        non_null(ptr).map(|ptr| *unsafe { Box::from_raw(ptr) })
556    }
557
558    /// Leaks a value to the heap
559    ///
560    /// [`None`] is represented by a null pointer
561    ///
562    /// The memory will conform to the [memory layout][1] used by [`Box`]
563    ///
564    /// # Usage
565    ///
566    /// ```rust
567    /// use ptr_cell::PtrCell;
568    ///
569    /// let ptr = PtrCell::heap_leak(Some(1155));
570    ///
571    /// assert_eq!(unsafe { PtrCell::heap_reclaim(ptr) }, Some(1155))
572    /// ```
573    ///
574    /// [1]: https://doc.rust-lang.org/std/boxed/index.html#memory-layout
575    #[inline]
576    #[must_use]
577    pub fn heap_leak(slot: Option<T>) -> *mut T {
578        match slot {
579            Some(value) => Box::into_raw(Box::new(value)),
580            None => core::ptr::null_mut(),
581        }
582    }
583}
584
585impl<T> core::fmt::Debug for PtrCell<T> {
586    fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
587        formatter
588            .debug_struct("PtrCell")
589            .field("value", &self.value)
590            .finish()
591    }
592}
593
594impl<T> Default for PtrCell<T> {
595    /// Constructs an empty cell
596    #[inline]
597    fn default() -> Self {
598        Self::new(None)
599    }
600}
601
602impl<T> Drop for PtrCell<T> {
603    #[inline]
604    fn drop(&mut self) {
605        let ptr = *self.value.get_mut();
606
607        unsafe { Self::heap_reclaim(ptr) };
608    }
609}
610
611impl<T> From<T> for PtrCell<T> {
612    #[inline]
613    fn from(value: T) -> Self {
614        Self::new(Some(value))
615    }
616}
617
618/// Returns `ptr` if it's non-null
619#[inline]
620fn non_null<T>(ptr: *mut T) -> Option<*mut T> {
621    if ptr.is_null() {
622        None
623    } else {
624        Some(ptr)
625    }
626}
627
628/// Memory ordering semantics for atomic operations
629///
630/// Each variant represents a group of compatible [orderings](Ordering). They determine how value
631/// updates are synchronized between threads
632#[non_exhaustive]
633#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
634pub enum Semantics {
635    /// [`Relaxed`](Ordering::Relaxed) semantics
636    ///
637    /// No synchronization constraints and the best performance
638    ///
639    /// Set this when using a value in only one thread
640    Relaxed,
641
642    /// [`Release`](Ordering::Release) - [`Acquire`](Ordering::Acquire) coupling semantics
643    ///
644    /// Mild synchronization constraints and fair performance
645    ///
646    /// A read will always see the preceding write (if one exists). Any operations that take place
647    /// before the write will also be seen, regardless of their semantics
648    ///
649    /// Set this when using a value in multiple threads, unless certain that you need different
650    /// semantics
651    #[default]
652    Coupled,
653
654    /// [`SeqCst`](Ordering::SeqCst) semantics
655    ///
656    /// Maximum synchronization constraints and the worst performance
657    ///
658    /// All memory operations will appear to be executed in a single, total order
659    Ordered,
660}
661
662/// Implements a method on [`Semantics`] that returns the appropriate [`Ordering`] for a type of
663/// operations
664macro_rules! operation {
665    ($name:ident with $coupled:path:
666        { $($overview:tt)* }, { $($returns:tt)* }, { $($assert:tt)* }
667    $(,)? ) => {
668        impl Semantics {
669            $($overview)*
670            ///
671            /// # Returns
672            /// - [`Relaxed`](Ordering::Relaxed) for [`Relaxed`](Semantics::Relaxed) semantics
673            $($returns)*
674            /// - [`SeqCst`](Ordering::SeqCst) for [`Ordered`](Semantics::Ordered) semantics
675            ///
676            /// # Usage
677            ///
678            /// ```rust
679            /// use ptr_cell::Semantics::Coupled;
680            /// use std::sync::atomic::Ordering;
681            ///
682            $($assert)*
683            /// ```
684            #[inline]
685            pub const fn $name(&self) -> Ordering {
686                match self {
687                    Self::Relaxed => Ordering::Relaxed,
688                    Self::Coupled => $coupled,
689                    Self::Ordered => Ordering::SeqCst,
690                }
691            }
692        }
693    };
694}
695
696// Asserts are missing a space on purpose. All whitespace after `///` seems to be carried over to
697// the example
698
699operation!(read_write with Ordering::AcqRel: {
700    /// Returns the memory ordering for read-write operations with these semantics
701}, {
702    /// - [`AcqRel`](Ordering::AcqRel) for [`Coupled`](Semantics::Coupled) semantics
703}, {
704    ///assert_eq!(Coupled.read_write(), Ordering::AcqRel)
705});
706
707operation!(write with Ordering::Release: {
708    /// Returns the memory ordering for write operations with these semantics
709}, {
710    /// - [`Release`](Ordering::Release) for [`Coupled`](Semantics::Coupled) semantics
711}, {
712    ///assert_eq!(Coupled.write(), Ordering::Release)
713});
714
715operation!(read with Ordering::Acquire: {
716    /// Returns the memory ordering for read operations with these semantics
717}, {
718    /// - [`Acquire`](Ordering::Acquire) for [`Coupled`](Semantics::Coupled) semantics
719}, {
720    ///assert_eq!(Coupled.read(), Ordering::Acquire)
721});