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::sync::atomic::Ordering;
40
41use bitflags::Flags;
42
43mod atomic_bits;
44#[cfg(test)]
45mod tests;
46
47pub use atomic_bits::AtomicBits;
48
49/// A flag set stored in an atomic integer.
50pub struct AtomicFlags<F>
51where
52 F: Flags,
53 F::Bits: AtomicBits,
54 F: Copy,
55{
56 storage: <F::Bits as AtomicBits>::Storage,
57}
58
59impl<F> Default for AtomicFlags<F>
60where
61 F: Flags,
62 F::Bits: AtomicBits,
63 F: Copy,
64 F: Default,
65{
66 fn default() -> Self {
67 Self::new(F::default())
68 }
69}
70
71impl<F> AtomicFlags<F>
72where
73 F: Flags,
74 F::Bits: AtomicBits,
75 F: Copy,
76{
77 pub fn new(flags: F) -> Self {
78 Self {
79 storage: F::Bits::new(flags.bits()),
80 }
81 }
82
83 pub fn store(&self, flags: F, ordering: Ordering) {
84 F::Bits::store(&self.storage, flags.bits(), ordering);
85 }
86
87 pub fn load(&self, ordering: Ordering) -> F {
88 F::from_bits_retain(F::Bits::load(&self.storage, ordering))
89 }
90
91 /// Atomically clears `flags`, returning the previous value.
92 pub fn fetch_and(&self, flags: F, ordering: Ordering) -> F {
93 F::from_bits_retain(F::Bits::fetch_and(&self.storage, flags.bits(), ordering))
94 }
95
96 /// Atomically sets `flags`, returning the previous value.
97 pub fn fetch_or(&self, flags: F, ordering: Ordering) -> F {
98 F::from_bits_retain(F::Bits::fetch_or(&self.storage, flags.bits(), ordering))
99 }
100
101 /// Atomically toggles `flags`, returning the previous value.
102 pub fn fetch_xor(&self, flags: F, ordering: Ordering) -> F {
103 F::from_bits_retain(F::Bits::fetch_xor(&self.storage, flags.bits(), ordering))
104 }
105
106 /// Atomically replaces the value with `!(current & flags)`, returning the
107 /// previous value.
108 ///
109 /// This follows the standard atomic `fetch_nand`: the result is not
110 /// restricted to `flags`, so bits outside the mask are set.
111 pub fn fetch_nand(&self, flags: F, ordering: Ordering) -> F {
112 F::from_bits_retain(F::Bits::fetch_nand(&self.storage, flags.bits(), ordering))
113 }
114
115 /// Atomically replaces the value with `flags`, returning the previous value.
116 pub fn swap(&self, flags: F, ordering: Ordering) -> F {
117 F::from_bits_retain(F::Bits::swap(&self.storage, flags.bits(), ordering))
118 }
119
120 /// Atomically clears `flags` and returns the subset that was set.
121 ///
122 /// Returns an empty set if none of `flags` were set. Other bits are left
123 /// untouched.
124 pub fn take(&self, flags: F, ordering: Ordering) -> F {
125 let previous = self.fetch_and(flags.complement(), ordering);
126 previous.intersection(flags)
127 }
128
129 /// Returns the subset of `flags` that is currently set.
130 ///
131 /// Does not modify the stored value. The result is a snapshot from a single
132 /// load; use [`take`](Self::take) to claim flags atomically.
133 pub fn peek(&self, flags: F, ordering: Ordering) -> F {
134 self.load(ordering).intersection(flags)
135 }
136
137 /// Clears all flags.
138 pub fn clear(&self, ordering: Ordering) {
139 self.store(F::empty(), ordering);
140 }
141
142 /// Consumes the atomic and returns the flags.
143 pub fn into_inner(self) -> F {
144 F::from_bits_retain(F::Bits::into_inner(self.storage))
145 }
146
147 /// Atomically sets the bits in `flags`, returning the previous value.
148 pub fn set(&self, flags: F, ordering: Ordering) -> F {
149 self.fetch_or(flags, ordering)
150 }
151
152 /// Atomically clears the bits in `flags`, returning the previous value.
153 pub fn unset(&self, flags: F, ordering: Ordering) -> F {
154 self.fetch_and(flags.complement(), ordering)
155 }
156}