shuttle_std/sync/atomic/int.rs
1use crate::sync::atomic::Atomic;
2use std::sync::atomic::Ordering;
3
4macro_rules! atomic_int {
5 ($name:ident, $int_type:ty) => {
6 /// An integer type which can be safely shared between threads.
7 pub struct $name {
8 inner: Atomic<$int_type>,
9 }
10
11 impl Default for $name {
12 fn default() -> Self {
13 Self::new(Default::default())
14 }
15 }
16
17 impl From<$int_type> for $name {
18 fn from(v: $int_type) -> Self {
19 Self::new(v)
20 }
21 }
22
23 impl std::fmt::Debug for $name {
24 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
25 std::fmt::Debug::fmt(unsafe { &self.raw_load() }, f)
26 }
27 }
28
29 impl $name {
30 /// Creates a new atomic integer.
31 #[track_caller]
32 pub const fn new(v: $int_type) -> Self {
33 Self {
34 inner: Atomic::new(v),
35 }
36 }
37
38 /// Returns a mutable reference to the underlying integer.
39 pub fn get_mut(&mut self) -> &mut $int_type {
40 self.inner.get_mut()
41 }
42
43 /// Consumes the atomic and returns the contained value.
44 pub fn into_inner(self) -> $int_type {
45 self.inner.into_inner()
46 }
47
48 /// Loads a value from the atomic integer.
49 pub fn load(&self, order: Ordering) -> $int_type {
50 self.inner.load(order)
51 }
52
53 /// Stores a value into the atomic integer.
54 pub fn store(&self, val: $int_type, order: Ordering) {
55 self.inner.store(val, order)
56 }
57
58 /// Stores a value into the atomic integer, returning the previous value.
59 pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type {
60 self.inner.swap(val, order)
61 }
62
63 /// Fetches the value, and applies a function to it that returns an optional new value.
64 /// Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
65 /// `Err(previous_value)`.
66 pub fn fetch_update<F>(
67 &self,
68 set_order: Ordering,
69 fetch_order: Ordering,
70 f: F,
71 ) -> Result<$int_type, $int_type>
72 where
73 F: FnMut($int_type) -> Option<$int_type>,
74 {
75 self.inner.fetch_update(set_order, fetch_order, f)
76 }
77
78 /// Stores a value into the atomic integer if the current value is the same as the
79 /// `current` value.
80 #[deprecated(
81 since = "0.0.6",
82 note = "Use `compare_exchange` or `compare_exchange_weak` instead"
83 )]
84 pub fn compare_and_swap(&self, current: $int_type, new: $int_type, order: Ordering) -> $int_type {
85 match self.compare_exchange(current, new, order, order) {
86 Ok(v) => v,
87 Err(v) => v,
88 }
89 }
90
91 /// Stores a value into the atomic integer if the current value is the same as the
92 /// `current` value.
93 ///
94 /// The return value is a result indicating whether the new value was written and
95 /// containing the previous value. On success this value is guaranteed to be equal to
96 /// `current`.
97 pub fn compare_exchange(
98 &self,
99 current: $int_type,
100 new: $int_type,
101 success: Ordering,
102 failure: Ordering,
103 ) -> Result<$int_type, $int_type> {
104 self.fetch_update(success, failure, |val| (val == current).then(|| new))
105 }
106
107 /// Stores a value into the atomic integer if the current value is the same as the
108 /// `current` value.
109 ///
110 /// Unlike `compare_exchange`, this function is allowed to spuriously fail even when
111 /// the comparison succeeds, which can result in more efficient code on some platforms.
112 /// The return value is a result indicating whether the new value was written and
113 /// containing the previous value.
114 // TODO actually produce spurious failures
115 pub fn compare_exchange_weak(
116 &self,
117 current: $int_type,
118 new: $int_type,
119 success: Ordering,
120 failure: Ordering,
121 ) -> Result<$int_type, $int_type> {
122 self.compare_exchange(current, new, success, failure)
123 }
124
125 /// Adds to the current value, returning the previous value.
126 ///
127 /// This operation wraps around on overflow.
128 pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type {
129 self.fetch_update(order, order, |old| Some(old.wrapping_add(val)))
130 .unwrap()
131 }
132
133 /// Subtracts from the current value, returning the previous value.
134 ///
135 /// This operation wraps around on overflow.
136 pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type {
137 self.fetch_update(order, order, |old| Some(old.wrapping_sub(val)))
138 .unwrap()
139 }
140
141 /// Bitwise "and" with the current value. Returns the previous value.
142 pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type {
143 self.fetch_update(order, order, |old| Some(old & val)).unwrap()
144 }
145
146 /// Bitwise "nand" with the current value. Returns the previous value.
147 pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type {
148 self.fetch_update(order, order, |old| Some(!(old & val))).unwrap()
149 }
150
151 /// Bitwise "or" with the current value. Returns the previous value.
152 pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type {
153 self.fetch_update(order, order, |old| Some(old | val)).unwrap()
154 }
155
156 /// Bitwise "xor" with the current value. Returns the previous value.
157 pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type {
158 self.fetch_update(order, order, |old| Some(old ^ val)).unwrap()
159 }
160
161 /// Maximum with the current value. Returns the previous value.
162 pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type {
163 self.fetch_update(order, order, |old| Some(old.max(val))).unwrap()
164 }
165
166 /// Minimum with the current value. Returns the previous value.
167 pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type {
168 self.fetch_update(order, order, |old| Some(old.min(val))).unwrap()
169 }
170
171 /// Load the atomic value directly without triggering any Shuttle context switches.
172 ///
173 /// # Safety
174 ///
175 /// Shuttle does not consider potential concurrent interleavings of this function call,
176 /// and so it should be used when those interleavings aren't important (primarily in
177 /// debugging scenarios where we might want to just print this atomic's value).
178 pub unsafe fn raw_load(&self) -> $int_type {
179 self.inner.raw_load()
180 }
181
182 #[cfg(test)]
183 pub(crate) fn signature(&self) -> crate::sync::ResourceSignature {
184 self.inner.signature()
185 }
186 }
187 };
188}
189
190atomic_int!(AtomicI8, i8);
191atomic_int!(AtomicI16, i16);
192atomic_int!(AtomicI32, i32);
193atomic_int!(AtomicI64, i64);
194atomic_int!(AtomicI128, i128);
195atomic_int!(AtomicIsize, isize);
196atomic_int!(AtomicU8, u8);
197atomic_int!(AtomicU16, u16);
198atomic_int!(AtomicU32, u32);
199atomic_int!(AtomicU64, u64);
200atomic_int!(AtomicU128, u128);
201atomic_int!(AtomicUsize, usize);