qubit_atomic/atomic/atomic_ref.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # Atomic Reference
11//!
12//! Provides an easy-to-use atomic reference type with sensible default memory
13//! orderings. Uses `Arc<T>` for thread-safe reference counting.
14//!
15//! # Author
16//!
17//! Haixing Hu
18
19use arc_swap::ArcSwap;
20use std::fmt;
21use std::sync::Arc;
22
23/// Atomic reference type.
24///
25/// Provides easy-to-use atomic operations on references with automatic memory
26/// ordering selection. Uses `Arc<T>` for thread-safe reference counting.
27///
28/// # Implementation Details
29///
30/// This type is backed by `arc_swap::ArcSwap<T>`, which provides lock-free,
31/// memory-safe atomic replacement and loading of `Arc<T>` values without
32/// exposing raw-pointer lifetime hazards.
33///
34/// # Features
35///
36/// - Automatic memory ordering selection
37/// - Thread-safe reference counting via `Arc`
38/// - Functional update operations
39/// - Zero-cost abstraction with inline methods
40///
41/// # Example
42///
43/// ```rust
44/// use qubit_atomic::AtomicRef;
45/// use std::sync::Arc;
46///
47/// #[derive(Debug, Clone)]
48/// struct Config {
49/// timeout: u64,
50/// max_retries: u32,
51/// }
52///
53/// let config = Arc::new(Config {
54/// timeout: 1000,
55/// max_retries: 3,
56/// });
57///
58/// let atomic_config = AtomicRef::new(config);
59///
60/// // Update configuration
61/// let new_config = Arc::new(Config {
62/// timeout: 2000,
63/// max_retries: 5,
64/// });
65///
66/// let old_config = atomic_config.swap(new_config);
67/// assert_eq!(old_config.timeout, 1000);
68/// assert_eq!(atomic_config.load().timeout, 2000);
69/// ```
70///
71/// # Author
72///
73/// Haixing Hu
74pub struct AtomicRef<T> {
75 /// Lock-free atomic storage for the current shared reference.
76 inner: ArcSwap<T>,
77}
78
79impl<T> AtomicRef<T> {
80 /// Creates a new atomic reference.
81 ///
82 /// # Parameters
83 ///
84 /// * `value` - The initial reference.
85 ///
86 /// # Returns
87 ///
88 /// An atomic reference initialized to `value`.
89 ///
90 /// # Example
91 ///
92 /// ```rust
93 /// use qubit_atomic::AtomicRef;
94 /// use std::sync::Arc;
95 ///
96 /// let data = Arc::new(42);
97 /// let atomic = AtomicRef::new(data);
98 /// assert_eq!(*atomic.load(), 42);
99 /// ```
100 #[inline]
101 pub fn new(value: Arc<T>) -> Self {
102 Self {
103 inner: ArcSwap::from(value),
104 }
105 }
106
107 /// Gets the current reference.
108 ///
109 /// # Returns
110 ///
111 /// A cloned `Arc` pointing to the current value.
112 ///
113 /// # Example
114 ///
115 /// ```rust
116 /// use qubit_atomic::AtomicRef;
117 /// use std::sync::Arc;
118 ///
119 /// let atomic = AtomicRef::new(Arc::new(42));
120 /// let value = atomic.load();
121 /// assert_eq!(*value, 42);
122 /// ```
123 #[inline]
124 pub fn load(&self) -> Arc<T> {
125 self.inner.load_full()
126 }
127
128 /// Sets a new reference.
129 ///
130 /// # Parameters
131 ///
132 /// * `value` - The new reference to set.
133 ///
134 /// # Example
135 ///
136 /// ```rust
137 /// use qubit_atomic::AtomicRef;
138 /// use std::sync::Arc;
139 ///
140 /// let atomic = AtomicRef::new(Arc::new(42));
141 /// atomic.store(Arc::new(100));
142 /// assert_eq!(*atomic.load(), 100);
143 /// ```
144 #[inline]
145 pub fn store(&self, value: Arc<T>) {
146 self.inner.store(value);
147 }
148
149 /// Swaps the current reference with a new reference, returning the old
150 /// reference.
151 ///
152 /// # Parameters
153 ///
154 /// * `value` - The new reference to swap in.
155 ///
156 /// # Returns
157 ///
158 /// The old reference.
159 ///
160 /// # Example
161 ///
162 /// ```rust
163 /// use qubit_atomic::AtomicRef;
164 /// use std::sync::Arc;
165 ///
166 /// let atomic = AtomicRef::new(Arc::new(10));
167 /// let old = atomic.swap(Arc::new(20));
168 /// assert_eq!(*old, 10);
169 /// assert_eq!(*atomic.load(), 20);
170 /// ```
171 #[inline]
172 pub fn swap(&self, value: Arc<T>) -> Arc<T> {
173 self.inner.swap(value)
174 }
175
176 /// Compares and sets the reference atomically.
177 ///
178 /// If the current reference equals `current` (by pointer equality), sets
179 /// it to `new` and returns `Ok(())`. Otherwise, returns `Err(actual)`
180 /// where `actual` is the current reference.
181 ///
182 /// # Parameters
183 ///
184 /// * `current` - The expected current reference.
185 /// * `new` - The new reference to set if current matches.
186 ///
187 /// # Returns
188 ///
189 /// `Ok(())` if the pointer comparison succeeds and `new` is stored.
190 ///
191 /// # Errors
192 ///
193 /// Returns `Err(actual)` with the observed current reference when the
194 /// pointer comparison fails. On failure, `new` is not installed.
195 ///
196 /// # Note
197 ///
198 /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
199 ///
200 /// # Example
201 ///
202 /// ```rust
203 /// use qubit_atomic::AtomicRef;
204 /// use std::sync::Arc;
205 ///
206 /// let atomic = AtomicRef::new(Arc::new(10));
207 /// let current = atomic.load();
208 ///
209 /// assert!(atomic.compare_set(¤t, Arc::new(20)).is_ok());
210 /// assert_eq!(*atomic.load(), 20);
211 /// ```
212 #[inline]
213 pub fn compare_set(&self, current: &Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
214 let prev = Arc::clone(&*self.inner.compare_and_swap(current, new));
215 if Arc::ptr_eq(&prev, current) {
216 Ok(())
217 } else {
218 Err(prev)
219 }
220 }
221
222 /// Weak version of compare-and-set.
223 ///
224 /// This implementation currently delegates to the same lock-free CAS as
225 /// `compare_set`, so it does not introduce extra spurious failures.
226 ///
227 /// # Parameters
228 ///
229 /// * `current` - The expected current reference.
230 /// * `new` - The new reference to set if current matches.
231 ///
232 /// # Returns
233 ///
234 /// `Ok(())` if the pointer comparison succeeds and `new` is stored.
235 ///
236 /// # Errors
237 ///
238 /// Returns `Err(actual)` with the observed current reference when the
239 /// pointer comparison fails. On failure, `new` is not installed. This
240 /// implementation currently delegates to [`compare_set`](Self::compare_set)
241 /// and does not add extra spurious failures.
242 ///
243 /// # Example
244 ///
245 /// ```rust
246 /// use qubit_atomic::AtomicRef;
247 /// use std::sync::Arc;
248 ///
249 /// let atomic = AtomicRef::new(Arc::new(10));
250 /// let mut current = atomic.load();
251 /// loop {
252 /// match atomic.compare_set_weak(¤t, Arc::new(20)) {
253 /// Ok(_) => break,
254 /// Err(actual) => current = actual,
255 /// }
256 /// }
257 /// assert_eq!(*atomic.load(), 20);
258 /// ```
259 #[inline]
260 pub fn compare_set_weak(&self, current: &Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
261 self.compare_set(current, new)
262 }
263
264 /// Compares and exchanges the reference atomically, returning the
265 /// previous reference.
266 ///
267 /// If the current reference equals `current` (by pointer equality), sets
268 /// it to `new` and returns the old reference. Otherwise, returns the
269 /// actual current reference.
270 ///
271 /// # Parameters
272 ///
273 /// * `current` - The expected current reference.
274 /// * `new` - The new reference to set if current matches.
275 ///
276 /// # Returns
277 ///
278 /// The reference before the operation.
279 ///
280 /// # Note
281 ///
282 /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
283 ///
284 /// # Example
285 ///
286 /// ```rust
287 /// use qubit_atomic::AtomicRef;
288 /// use std::sync::Arc;
289 ///
290 /// let atomic = AtomicRef::new(Arc::new(10));
291 /// let current = atomic.load();
292 ///
293 /// let prev = atomic.compare_and_exchange(¤t, Arc::new(20));
294 /// assert!(Arc::ptr_eq(&prev, ¤t));
295 /// assert_eq!(*atomic.load(), 20);
296 /// ```
297 #[inline]
298 pub fn compare_and_exchange(&self, current: &Arc<T>, new: Arc<T>) -> Arc<T> {
299 Arc::clone(&*self.inner.compare_and_swap(current, new))
300 }
301
302 /// Weak version of compare-and-exchange.
303 ///
304 /// This implementation currently delegates to the same lock-free CAS as
305 /// `compare_and_exchange`, so it does not introduce extra spurious
306 /// failures.
307 ///
308 /// # Parameters
309 ///
310 /// * `current` - The expected current reference.
311 /// * `new` - The new reference to set if current matches.
312 ///
313 /// # Returns
314 ///
315 /// The reference before the operation.
316 ///
317 /// # Example
318 ///
319 /// ```rust
320 /// use qubit_atomic::AtomicRef;
321 /// use std::sync::Arc;
322 ///
323 /// let atomic = AtomicRef::new(Arc::new(10));
324 /// let mut current = atomic.load();
325 /// loop {
326 /// let prev =
327 /// atomic.compare_and_exchange_weak(¤t, Arc::new(20));
328 /// if Arc::ptr_eq(&prev, ¤t) {
329 /// break;
330 /// }
331 /// current = prev;
332 /// }
333 /// assert_eq!(*atomic.load(), 20);
334 /// ```
335 #[inline]
336 pub fn compare_and_exchange_weak(&self, current: &Arc<T>, new: Arc<T>) -> Arc<T> {
337 self.compare_and_exchange(current, new)
338 }
339
340 /// Updates the reference using a function, returning the old reference.
341 ///
342 /// Internally uses a CAS loop until the update succeeds.
343 ///
344 /// # Parameters
345 ///
346 /// * `f` - A function that takes the current reference and returns the
347 /// new reference.
348 ///
349 /// # Returns
350 ///
351 /// The old reference before the update.
352 ///
353 /// # Example
354 ///
355 /// ```rust
356 /// use qubit_atomic::AtomicRef;
357 /// use std::sync::Arc;
358 ///
359 /// let atomic = AtomicRef::new(Arc::new(10));
360 /// let old = atomic.fetch_update(|x| Arc::new(**x * 2));
361 /// assert_eq!(*old, 10);
362 /// assert_eq!(*atomic.load(), 20);
363 /// ```
364 #[inline]
365 pub fn fetch_update<F>(&self, f: F) -> Arc<T>
366 where
367 F: Fn(&Arc<T>) -> Arc<T>,
368 {
369 let mut current = self.load();
370 loop {
371 let new = f(¤t);
372 match self.compare_set_weak(¤t, new) {
373 Ok(_) => return current,
374 Err(actual) => current = actual,
375 }
376 }
377 }
378
379 /// Gets a reference to the underlying `ArcSwap`.
380 ///
381 /// This allows advanced users to access lower-level `ArcSwap` APIs for
382 /// custom synchronization strategies.
383 ///
384 /// # Returns
385 ///
386 /// A reference to the underlying `arc_swap::ArcSwap<T>`.
387 #[inline]
388 pub fn inner(&self) -> &ArcSwap<T> {
389 &self.inner
390 }
391}
392
393impl<T> Clone for AtomicRef<T> {
394 /// Clones the atomic reference.
395 ///
396 /// Creates a new `AtomicRef` that initially points to the same value as
397 /// the original, but subsequent atomic operations are independent.
398 ///
399 /// # Returns
400 ///
401 /// A new atomic reference initialized with a clone of the currently loaded
402 /// `Arc`.
403 #[inline]
404 fn clone(&self) -> Self {
405 Self::new(self.load())
406 }
407}
408
409impl<T: fmt::Debug> fmt::Debug for AtomicRef<T> {
410 /// Formats the currently loaded reference for debugging.
411 ///
412 /// # Parameters
413 ///
414 /// * `f` - The formatter receiving the debug representation.
415 ///
416 /// # Returns
417 ///
418 /// A formatting result from the formatter.
419 #[inline]
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 f.debug_struct("AtomicRef")
422 .field("value", &self.load())
423 .finish()
424 }
425}
426
427impl<T: fmt::Display> fmt::Display for AtomicRef<T> {
428 /// Formats the currently loaded reference with display formatting.
429 ///
430 /// # Parameters
431 ///
432 /// * `f` - The formatter receiving the displayed value.
433 ///
434 /// # Returns
435 ///
436 /// A formatting result from the formatter.
437 #[inline]
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 write!(f, "{}", self.load())
440 }
441}