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 try_update(
67 &self,
68 set_order: Ordering,
69 fetch_order: Ordering,
70 mut f: impl FnMut($int_type) -> Option<$int_type>,
71 ) -> Result<$int_type, $int_type> {
72 let mut prev = self.load(fetch_order);
73 while let Some(next) = f(prev) {
74 match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
75 x @ Ok(_) => return x,
76 Err(next_prev) => prev = next_prev,
77 }
78 }
79 Err(prev)
80 }
81
82 /// Fetches the value, and applies a function to it that returns an optional new value.
83 ///
84 /// Since Rust 1.95 this is an alias for [`try_update`](Self::try_update). Deprecated
85 /// since Rust 1.99.
86 pub fn fetch_update<F>(
87 &self,
88 set_order: Ordering,
89 fetch_order: Ordering,
90 f: F,
91 ) -> Result<$int_type, $int_type>
92 where
93 F: FnMut($int_type) -> Option<$int_type>,
94 {
95 self.try_update(set_order, fetch_order, f)
96 }
97
98 /// Fetches the value, applies a function to it that returns a new value, and stores the
99 /// new value. Returns the previous value.
100 pub fn update(
101 &self,
102 set_order: Ordering,
103 fetch_order: Ordering,
104 mut f: impl FnMut($int_type) -> $int_type,
105 ) -> $int_type {
106 let mut prev = self.load(fetch_order);
107 loop {
108 match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) {
109 Ok(x) => break x,
110 Err(next_prev) => prev = next_prev,
111 }
112 }
113 }
114
115 /// Stores a value into the atomic integer if the current value is the same as the
116 /// `current` value.
117 #[deprecated(
118 since = "0.0.6",
119 note = "Use `compare_exchange` or `compare_exchange_weak` instead"
120 )]
121 pub fn compare_and_swap(&self, current: $int_type, new: $int_type, order: Ordering) -> $int_type {
122 match self.compare_exchange(current, new, order, order) {
123 Ok(v) => v,
124 Err(v) => v,
125 }
126 }
127
128 /// Stores a value into the atomic integer if the current value is the same as the
129 /// `current` value.
130 ///
131 /// The return value is a result indicating whether the new value was written and
132 /// containing the previous value. On success this value is guaranteed to be equal to
133 /// `current`.
134 pub fn compare_exchange(
135 &self,
136 current: $int_type,
137 new: $int_type,
138 success: Ordering,
139 failure: Ordering,
140 ) -> Result<$int_type, $int_type> {
141 self.inner
142 .try_update(success, failure, |val| (val == current).then(|| new))
143 }
144
145 /// Stores a value into the atomic integer if the current value is the same as the
146 /// `current` value.
147 ///
148 /// Unlike `compare_exchange`, this function is allowed to spuriously fail even when
149 /// the comparison succeeds, which can result in more efficient code on some platforms.
150 /// The return value is a result indicating whether the new value was written and
151 /// containing the previous value.
152 // TODO actually produce spurious failures
153 pub fn compare_exchange_weak(
154 &self,
155 current: $int_type,
156 new: $int_type,
157 success: Ordering,
158 failure: Ordering,
159 ) -> Result<$int_type, $int_type> {
160 self.compare_exchange(current, new, success, failure)
161 }
162
163 /// Adds to the current value, returning the previous value.
164 ///
165 /// This operation wraps around on overflow.
166 pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type {
167 self.inner
168 .try_update(order, order, |old| Some(old.wrapping_add(val)))
169 .unwrap()
170 }
171
172 /// Subtracts from the current value, returning the previous value.
173 ///
174 /// This operation wraps around on overflow.
175 pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type {
176 self.inner
177 .try_update(order, order, |old| Some(old.wrapping_sub(val)))
178 .unwrap()
179 }
180
181 /// Bitwise "and" with the current value. Returns the previous value.
182 pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type {
183 self.inner
184 .try_update(order, order, |old| Some(old & val))
185 .unwrap()
186 }
187
188 /// Bitwise "nand" with the current value. Returns the previous value.
189 pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type {
190 self.inner
191 .try_update(order, order, |old| Some(!(old & val)))
192 .unwrap()
193 }
194
195 /// Bitwise "or" with the current value. Returns the previous value.
196 pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type {
197 self.inner
198 .try_update(order, order, |old| Some(old | val))
199 .unwrap()
200 }
201
202 /// Bitwise "xor" with the current value. Returns the previous value.
203 pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type {
204 self.inner
205 .try_update(order, order, |old| Some(old ^ val))
206 .unwrap()
207 }
208
209 /// Maximum with the current value. Returns the previous value.
210 pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type {
211 self.inner
212 .try_update(order, order, |old| Some(old.max(val)))
213 .unwrap()
214 }
215
216 /// Minimum with the current value. Returns the previous value.
217 pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type {
218 self.inner
219 .try_update(order, order, |old| Some(old.min(val)))
220 .unwrap()
221 }
222
223 /// Load the atomic value directly without triggering any Shuttle context switches.
224 ///
225 /// # Safety
226 ///
227 /// Shuttle does not consider potential concurrent interleavings of this function call,
228 /// and so it should be used when those interleavings aren't important (primarily in
229 /// debugging scenarios where we might want to just print this atomic's value).
230 pub unsafe fn raw_load(&self) -> $int_type {
231 self.inner.raw_load()
232 }
233
234 #[cfg(test)]
235 pub(crate) fn signature(&self) -> crate::sync::ResourceSignature {
236 self.inner.signature()
237 }
238 }
239 };
240}
241
242atomic_int!(AtomicI8, i8);
243atomic_int!(AtomicI16, i16);
244atomic_int!(AtomicI32, i32);
245atomic_int!(AtomicI64, i64);
246atomic_int!(AtomicI128, i128);
247atomic_int!(AtomicIsize, isize);
248atomic_int!(AtomicU8, u8);
249atomic_int!(AtomicU16, u16);
250atomic_int!(AtomicU32, u32);
251atomic_int!(AtomicU64, u64);
252atomic_int!(AtomicU128, u128);
253atomic_int!(AtomicUsize, usize);