1pub 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 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 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 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}