Skip to main content

atomic_bitflags/
lib.rs

1//! Atomic storage for a [`bitflags`] flag set.
2//!
3//! [`AtomicFlags<F>`] keeps the bits of a `bitflags` type in an atomic
4//! integer, so a flag set can be shared between threads without a lock.
5//!
6//! Every operation on the stored value takes an [`Ordering`] under the same
7//! rules as the standard library atomics. Flags wider than 64 bits are not
8//! supported, and 64-bit atomics are not available on every target.
9//!
10//! # Examples
11//!
12//! ```
13//! use atomic_bitflags::AtomicFlags;
14//! use bitflags::bitflags;
15//! use std::sync::Arc;
16//! use std::sync::atomic::Ordering;
17//!
18//! bitflags! {
19//!     #[derive(Debug, Clone, Copy, PartialEq, Eq)]
20//!     struct Features: u8 {
21//!         const FAST = 0b001;
22//!         const SAFE = 0b010;
23//!     }
24//! }
25//!
26//! let flags = Arc::new(AtomicFlags::new(Features::FAST));
27//! let remote = Arc::clone(&flags);
28//!
29//! std::thread::spawn(move || {
30//!     remote.set(Features::SAFE, Ordering::Relaxed);
31//! })
32//! .join()
33//! .unwrap();
34//!
35//! let current = flags.load(Ordering::Relaxed);
36//! assert!(current.contains(Features::FAST | Features::SAFE));
37//! ```
38
39use std::fmt;
40use std::sync::atomic::Ordering;
41
42use bitflags::Flags;
43
44mod atomic_bits;
45#[cfg(test)]
46mod tests;
47
48pub use atomic_bits::AtomicBits;
49
50/// A flag set stored in an atomic integer.
51pub struct AtomicFlags<F>
52where
53    F: Flags,
54    F::Bits: AtomicBits,
55    F: Copy,
56{
57    storage: <F::Bits as AtomicBits>::Storage,
58}
59
60impl<F> Default for AtomicFlags<F>
61where
62    F: Flags,
63    F::Bits: AtomicBits,
64    F: Copy,
65    F: Default,
66{
67    fn default() -> Self {
68        Self::new(F::default())
69    }
70}
71
72impl<F> fmt::Debug for AtomicFlags<F>
73where
74    F: Flags,
75    F::Bits: AtomicBits,
76    F: Copy,
77    F: fmt::Debug,
78{
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        self.load(Ordering::SeqCst).fmt(f)
81    }
82}
83
84impl<F> AtomicFlags<F>
85where
86    F: Flags,
87    F::Bits: AtomicBits,
88    F: Copy,
89{
90    pub fn new(flags: F) -> Self {
91        Self {
92            storage: F::Bits::new(flags.bits()),
93        }
94    }
95
96    pub fn store(&self, flags: F, ordering: Ordering) {
97        F::Bits::store(&self.storage, flags.bits(), ordering);
98    }
99
100    pub fn load(&self, ordering: Ordering) -> F {
101        F::from_bits_retain(F::Bits::load(&self.storage, ordering))
102    }
103
104    /// Atomically clears `flags`, returning the previous value.
105    pub fn fetch_and(&self, flags: F, ordering: Ordering) -> F {
106        F::from_bits_retain(F::Bits::fetch_and(&self.storage, flags.bits(), ordering))
107    }
108
109    /// Atomically sets `flags`, returning the previous value.
110    pub fn fetch_or(&self, flags: F, ordering: Ordering) -> F {
111        F::from_bits_retain(F::Bits::fetch_or(&self.storage, flags.bits(), ordering))
112    }
113
114    /// Atomically toggles `flags`, returning the previous value.
115    pub fn fetch_xor(&self, flags: F, ordering: Ordering) -> F {
116        F::from_bits_retain(F::Bits::fetch_xor(&self.storage, flags.bits(), ordering))
117    }
118
119    /// Atomically replaces the value with `!(current & flags)`, returning the
120    /// previous value.
121    ///
122    /// This follows the standard atomic `fetch_nand`: the result is not
123    /// restricted to `flags`, so bits outside the mask are set.
124    pub fn fetch_nand(&self, flags: F, ordering: Ordering) -> F {
125        F::from_bits_retain(F::Bits::fetch_nand(&self.storage, flags.bits(), ordering))
126    }
127
128    /// Atomically replaces the value with `flags`, returning the previous value.
129    pub fn swap(&self, flags: F, ordering: Ordering) -> F {
130        F::from_bits_retain(F::Bits::swap(&self.storage, flags.bits(), ordering))
131    }
132
133    /// Atomically clears `flags` and returns the subset that was set.
134    ///
135    /// Returns an empty set if none of `flags` were set. Other bits are left
136    /// untouched.
137    pub fn take(&self, flags: F, ordering: Ordering) -> F {
138        let previous = self.fetch_and(flags.complement(), ordering);
139        previous.intersection(flags)
140    }
141
142    /// Returns the subset of `flags` that is currently set.
143    ///
144    /// Does not modify the stored value. The result is a snapshot from a single
145    /// load; use [`take`](Self::take) to claim flags atomically.
146    pub fn peek(&self, flags: F, ordering: Ordering) -> F {
147        self.load(ordering).intersection(flags)
148    }
149
150    /// Clears all flags.
151    pub fn clear(&self, ordering: Ordering) {
152        self.store(F::empty(), ordering);
153    }
154
155    /// Consumes the atomic and returns the flags.
156    pub fn into_inner(self) -> F {
157        F::from_bits_retain(F::Bits::into_inner(self.storage))
158    }
159
160    /// Atomically sets the bits in `flags`, returning the previous value.
161    pub fn set(&self, flags: F, ordering: Ordering) -> F {
162        self.fetch_or(flags, ordering)
163    }
164
165    /// Atomically clears the bits in `flags`, returning the previous value.
166    pub fn unset(&self, flags: F, ordering: Ordering) -> F {
167        self.fetch_and(flags.complement(), ordering)
168    }
169}