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    #[inline]
91    pub fn new(flags: F) -> Self {
92        Self::from_storage(F::Bits::new(flags.bits()))
93    }
94
95    /// Const constructor from an already-built atomic.
96    ///
97    /// Less ergonomic than `new` but allow to build this struct from a const context
98    /// (i.e. static flags).
99    ///
100    /// ```
101    /// use std::sync::atomic::AtomicU8;
102    /// use atomic_bitflags::AtomicFlags;
103    /// use bitflags::bitflags;
104    ///
105    /// bitflags! {
106    ///     #[derive(Clone, Copy)]
107    ///     struct Features: u8 {
108    ///         const CACHE = 1 << 0;
109    ///     }
110    /// }
111    ///
112    /// static FEATURES: AtomicFlags<Features> =
113    ///     AtomicFlags::from_storage(AtomicU8::new(Features::CACHE.bits()));
114    /// ```
115    pub const fn from_storage(storage: <F::Bits as AtomicBits>::Storage) -> Self {
116        Self { storage }
117    }
118
119    #[inline]
120    pub fn store(&self, flags: F, ordering: Ordering) {
121        F::Bits::store(&self.storage, flags.bits(), ordering);
122    }
123
124    #[inline]
125    pub fn load(&self, ordering: Ordering) -> F {
126        F::from_bits_retain(F::Bits::load(&self.storage, ordering))
127    }
128
129    /// Atomically replaces the value with `current & flags` returning the previous value.
130    #[inline]
131    pub fn fetch_and(&self, flags: F, ordering: Ordering) -> F {
132        F::from_bits_retain(F::Bits::fetch_and(&self.storage, flags.bits(), ordering))
133    }
134
135    /// Atomically replaces the value with `current | flags`, returning the
136    /// previous value.
137    #[inline]
138    pub fn fetch_or(&self, flags: F, ordering: Ordering) -> F {
139        F::from_bits_retain(F::Bits::fetch_or(&self.storage, flags.bits(), ordering))
140    }
141
142    /// Atomically toggles `flags`, returning the previous value.
143    #[inline]
144    pub fn fetch_xor(&self, flags: F, ordering: Ordering) -> F {
145        F::from_bits_retain(F::Bits::fetch_xor(&self.storage, flags.bits(), ordering))
146    }
147
148    /// Synonymous with `fetch_xor`.
149    #[inline]
150    pub fn toggle(&self, flags: F, ordering: Ordering) -> F {
151        self.fetch_xor(flags, ordering)
152    }
153
154    /// Atomically replaces the value with `!(current & flags)`, returning the
155    /// previous value.
156    ///
157    /// This follows the standard atomic `fetch_nand`: the result is not
158    /// restricted to `flags`, so bits outside the mask are set.
159    #[inline]
160    pub fn fetch_nand(&self, flags: F, ordering: Ordering) -> F {
161        F::from_bits_retain(F::Bits::fetch_nand(&self.storage, flags.bits(), ordering))
162    }
163
164    /// Atomically replaces the value with `flags`, returning the previous value.
165    #[inline]
166    pub fn swap(&self, flags: F, ordering: Ordering) -> F {
167        F::from_bits_retain(F::Bits::swap(&self.storage, flags.bits(), ordering))
168    }
169
170    /// Atomically stores `new` if the current value is `current`.
171    ///
172    /// Returns `Ok(previous)` on success and `Err(actual)` on failure.
173    ///
174    /// # Panics
175    ///
176    /// Panics if `failure` is stronger than `success`.
177    #[inline]
178    pub fn compare_exchange(
179        &self,
180        current: F,
181        new: F,
182        success: Ordering,
183        failure: Ordering,
184    ) -> Result<F, F> {
185        F::Bits::compare_exchange(&self.storage, current.bits(), new.bits(), success, failure)
186            .map(F::from_bits_retain)
187            .map_err(F::from_bits_retain)
188    }
189
190    /// Like [`compare_exchange`](Self::compare_exchange), but may fail
191    /// spuriously.
192    ///
193    /// Use it inside a retry loop where a spurious failure is harmless; prefer
194    /// [`compare_exchange`](Self::compare_exchange) otherwise.
195    ///
196    /// # Panics
197    ///
198    /// Panics if `failure` is stronger than `success`.
199    #[inline]
200    pub fn compare_exchange_weak(
201        &self,
202        current: F,
203        new: F,
204        success: Ordering,
205        failure: Ordering,
206    ) -> Result<F, F> {
207        F::Bits::compare_exchange_weak(&self.storage, current.bits(), new.bits(), success, failure)
208            .map(F::from_bits_retain)
209            .map_err(F::from_bits_retain)
210    }
211
212    /// Atomically updates the value using `f`, returning the previous value.
213    ///
214    /// `f` receives the current value and returns the new one, or `None` to
215    /// abort. It may run more than once if another thread changes the value.
216    ///
217    /// Returns `Ok(previous)` on success and `Err(current)` if `f` returns
218    /// `None`.
219    ///
220    /// # Panics
221    ///
222    /// Panics if `fetch_order` is `Release` or `AcqRel`, or stronger than
223    /// `set_order`.
224    #[inline]
225    pub fn try_update<Op>(
226        &self,
227        set_order: Ordering,
228        fetch_order: Ordering,
229        mut f: Op,
230    ) -> Result<F, F>
231    where
232        Op: FnMut(F) -> Option<F>,
233    {
234        let mut previous = self.load(fetch_order);
235        loop {
236            let new = match f(previous) {
237                Some(new) => new,
238                None => return Err(previous),
239            };
240            match self.compare_exchange_weak(previous, new, set_order, fetch_order) {
241                Ok(previous) => return Ok(previous),
242                Err(actual) => previous = actual,
243            }
244        }
245    }
246
247    /// Atomically clears `flags` and returns the subset that was set.
248    ///
249    /// Returns an empty set if none of `flags` were set. Other bits are left
250    /// untouched.
251    #[inline]
252    pub fn take(&self, flags: F, ordering: Ordering) -> F {
253        let complement = Self::retained_complement(flags);
254        let previous = self.fetch_and(complement, ordering);
255        previous.intersection(flags)
256    }
257
258    /// Returns the subset of `flags` that is currently set.
259    ///
260    /// Does not modify the stored value. The result is a snapshot from a single
261    /// load; use [`take`](Self::take) to claim flags atomically.
262    #[inline]
263    pub fn peek(&self, flags: F, ordering: Ordering) -> F {
264        self.load(ordering).intersection(flags)
265    }
266
267    /// Clears all flags.
268    #[inline]
269    pub fn clear(&self, ordering: Ordering) {
270        self.store(F::empty(), ordering);
271    }
272
273    /// Consumes the atomic and returns the flags.
274    #[inline]
275    pub fn into_inner(self) -> F {
276        F::from_bits_retain(F::Bits::into_inner(self.storage))
277    }
278
279    /// Atomically sets the bits in `flags`, returning the previous value.
280    #[inline]
281    pub fn set(&self, flags: F, ordering: Ordering) -> F {
282        self.fetch_or(flags, ordering)
283    }
284
285    /// Atomically clears the bits in `flags`, returning the previous value.
286    #[inline]
287    pub fn unset(&self, flags: F, ordering: Ordering) -> F {
288        let complement = Self::retained_complement(flags);
289        self.fetch_and(complement, ordering)
290    }
291
292    /// Return the complement with retained instead of truncated mode.
293    ///
294    /// This is useful for take & unset that would otherwise clear unknown
295    /// bits, contradicting the documentation.
296    #[inline]
297    fn retained_complement(flags: F) -> F {
298        F::from_bits_retain(!flags.bits())
299    }
300}