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}