Skip to main content

atomic_bitflags/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub use {bitflags as __bitflags, paste};
6
7#[macro_export]
8macro_rules! atomic_bitflags {
9    (
10        $(#[$outer:meta])*
11        $vis:vis struct $BitFlags:ident: $T:ty {
12            $($t:tt)*
13        }
14    ) => {
15        $crate::paste::paste! {
16            $crate::__bitflags::bitflags! {
17                $(#[$outer])*
18                $vis struct $BitFlags: $T {
19                    $($t)*
20                }
21            }
22
23            #[allow(dead_code)]
24            #[derive(Debug, Default)]
25            $vis struct [<Atomic $BitFlags>] {
26                inner: std::sync::atomic::[<Atomic $T:camel>],
27            }
28
29            #[allow(dead_code)]
30            impl [<Atomic $BitFlags>] {
31                pub fn new(initial: $BitFlags) -> Self {
32                    Self {
33                        inner: std::sync::atomic::[<Atomic $T:camel>]::new(initial.bits()),
34                    }
35                }
36
37                pub fn load(&self, order: std::sync::atomic::Ordering) -> $BitFlags {
38                    $BitFlags::from_bits_truncate(self.inner.load(order))
39                }
40
41                pub fn store(&self, val: $BitFlags, order: std::sync::atomic::Ordering) {
42                    self.inner.store(val.bits(), order);
43                }
44
45                pub fn fetch_or(&self, val: $BitFlags, order: std::sync::atomic::Ordering) -> $BitFlags {
46                    $BitFlags::from_bits_truncate(self.inner.fetch_or(val.bits(), order))
47                }
48
49                pub fn fetch_and(&self, val: $BitFlags, order: std::sync::atomic::Ordering) -> $BitFlags {
50                    $BitFlags::from_bits_truncate(self.inner.fetch_and(val.bits(), order))
51                }
52
53                pub fn swap(&self, val: $BitFlags, order: std::sync::atomic::Ordering) -> $BitFlags {
54                    $BitFlags::from_bits_truncate(self.inner.swap(val.bits(), order))
55                }
56
57                pub fn compare_exchange(
58                    &self,
59                    current: $BitFlags,
60                    new: $BitFlags,
61                    success: std::sync::atomic::Ordering,
62                    failure: std::sync::atomic::Ordering,
63                ) -> Result<$BitFlags, $BitFlags> {
64                    self.inner.compare_exchange(current.bits(), new.bits(), success, failure)
65                        .map($BitFlags::from_bits_truncate)
66                        .map_err($BitFlags::from_bits_truncate)
67                }
68
69                pub fn update(
70                    &self,
71                    value: $BitFlags,
72                    mask: $BitFlags,
73                    set_order: std::sync::atomic::Ordering,
74                    fetch_order: std::sync::atomic::Ordering,
75                ) -> $BitFlags {
76                    self.inner.fetch_update(set_order, fetch_order, |old| {
77                        Some((old & !mask.bits()) | (value.bits() & mask.bits()))
78                    }).map($BitFlags::from_bits_truncate).unwrap()
79                }
80            }
81
82            impl From<$BitFlags> for [<Atomic $BitFlags>] {
83                fn from(initial: $BitFlags) -> Self {
84                    Self::new(initial)
85                }
86            }
87        }
88    };
89}
90
91#[cfg(test)]
92mod tests {
93    use std::sync::atomic::Ordering;
94
95    atomic_bitflags! {
96        #[derive(PartialEq, Eq, Debug, Clone, Copy)]
97        pub struct TestFlags: u32 {
98            const A = 0x1;
99            const B = 0x2;
100            const C = 0x4;
101        }
102    }
103
104    #[test]
105    fn test_atomic_bitflags() {
106        let atomic = AtomicTestFlags::new(TestFlags::A);
107        assert_eq!(atomic.load(Ordering::Relaxed), TestFlags::A);
108
109        atomic.store(TestFlags::B, Ordering::Relaxed);
110        assert_eq!(atomic.load(Ordering::Relaxed), TestFlags::B);
111
112        let prev = atomic.fetch_or(TestFlags::C, Ordering::Relaxed);
113        assert_eq!(prev, TestFlags::B);
114        assert_eq!(atomic.load(Ordering::Relaxed), TestFlags::B | TestFlags::C);
115
116        let prev = atomic.fetch_and(TestFlags::C, Ordering::Relaxed);
117        assert_eq!(prev, TestFlags::B | TestFlags::C);
118        assert_eq!(atomic.load(Ordering::Relaxed), TestFlags::C);
119    }
120
121    #[test]
122    fn test_update() {
123        let atomic = AtomicTestFlags::new(TestFlags::A | TestFlags::B);
124
125        // Update A to 0, leaving B as is. Mask is A. Value is 0.
126        let prev =
127            atomic.update(TestFlags::empty(), TestFlags::A, Ordering::Relaxed, Ordering::Relaxed);
128        assert_eq!(prev, TestFlags::A | TestFlags::B);
129        assert_eq!(atomic.load(Ordering::Relaxed), TestFlags::B);
130
131        // Update A to 1, leaving B as is. Mask is A. Value is A.
132        let prev = atomic.update(TestFlags::A, TestFlags::A, Ordering::Relaxed, Ordering::Relaxed);
133        assert_eq!(prev, TestFlags::B);
134        assert_eq!(atomic.load(Ordering::Relaxed), TestFlags::A | TestFlags::B);
135
136        // Update B to 0, A to 0. Mask is A | B. Value is 0.
137        let prev = atomic.update(
138            TestFlags::empty(),
139            TestFlags::A | TestFlags::B,
140            Ordering::Relaxed,
141            Ordering::Relaxed,
142        );
143        assert_eq!(prev, TestFlags::A | TestFlags::B);
144        assert_eq!(atomic.load(Ordering::Relaxed), TestFlags::empty());
145    }
146
147    #[test]
148    fn test_from() {
149        let atomic: AtomicTestFlags = TestFlags::A.into();
150        assert_eq!(atomic.load(Ordering::Relaxed), TestFlags::A);
151    }
152}