Skip to main content

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    /// Creates a new atomic reference from an owned value.
108    ///
109    /// This is a convenience constructor for callers that do not already have
110    /// an [`Arc<T>`]. It wraps `value` in [`Arc::new`] and then delegates to
111    /// [`new`](Self::new).
112    ///
113    /// # Parameters
114    ///
115    /// * `value` - The owned value to store as the initial reference.
116    ///
117    /// # Returns
118    ///
119    /// An atomic reference initialized to `Arc::new(value)`.
120    ///
121    /// # Example
122    ///
123    /// ```rust
124    /// use qubit_atomic::AtomicRef;
125    ///
126    /// let atomic = AtomicRef::from_value(42);
127    /// assert_eq!(*atomic.load(), 42);
128    /// ```
129    #[inline]
130    pub fn from_value(value: T) -> Self {
131        Self::new(Arc::new(value))
132    }
133
134    /// Gets the current reference.
135    ///
136    /// # Returns
137    ///
138    /// A cloned `Arc` pointing to the current value.
139    ///
140    /// # Example
141    ///
142    /// ```rust
143    /// use qubit_atomic::AtomicRef;
144    /// use std::sync::Arc;
145    ///
146    /// let atomic = AtomicRef::new(Arc::new(42));
147    /// let value = atomic.load();
148    /// assert_eq!(*value, 42);
149    /// ```
150    #[inline]
151    pub fn load(&self) -> Arc<T> {
152        self.inner.load_full()
153    }
154
155    /// Sets a new reference.
156    ///
157    /// # Parameters
158    ///
159    /// * `value` - The new reference to set.
160    ///
161    /// # Example
162    ///
163    /// ```rust
164    /// use qubit_atomic::AtomicRef;
165    /// use std::sync::Arc;
166    ///
167    /// let atomic = AtomicRef::new(Arc::new(42));
168    /// atomic.store(Arc::new(100));
169    /// assert_eq!(*atomic.load(), 100);
170    /// ```
171    #[inline]
172    pub fn store(&self, value: Arc<T>) {
173        self.inner.store(value);
174    }
175
176    /// Swaps the current reference with a new reference, returning the old
177    /// reference.
178    ///
179    /// # Parameters
180    ///
181    /// * `value` - The new reference to swap in.
182    ///
183    /// # Returns
184    ///
185    /// The old reference.
186    ///
187    /// # Example
188    ///
189    /// ```rust
190    /// use qubit_atomic::AtomicRef;
191    /// use std::sync::Arc;
192    ///
193    /// let atomic = AtomicRef::new(Arc::new(10));
194    /// let old = atomic.swap(Arc::new(20));
195    /// assert_eq!(*old, 10);
196    /// assert_eq!(*atomic.load(), 20);
197    /// ```
198    #[inline]
199    pub fn swap(&self, value: Arc<T>) -> Arc<T> {
200        self.inner.swap(value)
201    }
202
203    /// Compares and sets the reference atomically.
204    ///
205    /// If the current reference equals `current` (by pointer equality), sets
206    /// it to `new` and returns `Ok(())`. Otherwise, returns `Err(actual)`
207    /// where `actual` is the current reference.
208    ///
209    /// # Parameters
210    ///
211    /// * `current` - The expected current reference.
212    /// * `new` - The new reference to set if current matches.
213    ///
214    /// # Returns
215    ///
216    /// `Ok(())` if the pointer comparison succeeds and `new` is stored.
217    ///
218    /// # Errors
219    ///
220    /// Returns `Err(actual)` with the observed current reference when the
221    /// pointer comparison fails. On failure, `new` is not installed.
222    ///
223    /// # Note
224    ///
225    /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
226    ///
227    /// # Example
228    ///
229    /// ```rust
230    /// use qubit_atomic::AtomicRef;
231    /// use std::sync::Arc;
232    ///
233    /// let atomic = AtomicRef::new(Arc::new(10));
234    /// let current = atomic.load();
235    ///
236    /// assert!(atomic.compare_set(&current, Arc::new(20)).is_ok());
237    /// assert_eq!(*atomic.load(), 20);
238    /// ```
239    #[inline]
240    pub fn compare_set(&self, current: &Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
241        let prev = Arc::clone(&*self.inner.compare_and_swap(current, new));
242        if Arc::ptr_eq(&prev, current) {
243            Ok(())
244        } else {
245            Err(prev)
246        }
247    }
248
249    /// Weak version of compare-and-set.
250    ///
251    /// This implementation currently delegates to the same lock-free CAS as
252    /// `compare_set`, so it does not introduce extra spurious failures.
253    ///
254    /// # Parameters
255    ///
256    /// * `current` - The expected current reference.
257    /// * `new` - The new reference to set if current matches.
258    ///
259    /// # Returns
260    ///
261    /// `Ok(())` if the pointer comparison succeeds and `new` is stored.
262    ///
263    /// # Errors
264    ///
265    /// Returns `Err(actual)` with the observed current reference when the
266    /// pointer comparison fails. On failure, `new` is not installed. This
267    /// implementation currently delegates to [`compare_set`](Self::compare_set)
268    /// and does not add extra spurious failures.
269    ///
270    /// # Example
271    ///
272    /// ```rust
273    /// use qubit_atomic::AtomicRef;
274    /// use std::sync::Arc;
275    ///
276    /// let atomic = AtomicRef::new(Arc::new(10));
277    /// let mut current = atomic.load();
278    /// loop {
279    ///     match atomic.compare_set_weak(&current, Arc::new(20)) {
280    ///         Ok(_) => break,
281    ///         Err(actual) => current = actual,
282    ///     }
283    /// }
284    /// assert_eq!(*atomic.load(), 20);
285    /// ```
286    #[inline]
287    pub fn compare_set_weak(&self, current: &Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
288        self.compare_set(current, new)
289    }
290
291    /// Compares and exchanges the reference atomically, returning the
292    /// previous reference.
293    ///
294    /// If the current reference equals `current` (by pointer equality), sets
295    /// it to `new` and returns the old reference. Otherwise, returns the
296    /// actual current reference.
297    ///
298    /// # Parameters
299    ///
300    /// * `current` - The expected current reference.
301    /// * `new` - The new reference to set if current matches.
302    ///
303    /// # Returns
304    ///
305    /// The reference before the operation.
306    ///
307    /// # Note
308    ///
309    /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
310    ///
311    /// # Example
312    ///
313    /// ```rust
314    /// use qubit_atomic::AtomicRef;
315    /// use std::sync::Arc;
316    ///
317    /// let atomic = AtomicRef::new(Arc::new(10));
318    /// let current = atomic.load();
319    ///
320    /// let prev = atomic.compare_and_exchange(&current, Arc::new(20));
321    /// assert!(Arc::ptr_eq(&prev, &current));
322    /// assert_eq!(*atomic.load(), 20);
323    /// ```
324    #[inline]
325    pub fn compare_and_exchange(&self, current: &Arc<T>, new: Arc<T>) -> Arc<T> {
326        Arc::clone(&*self.inner.compare_and_swap(current, new))
327    }
328
329    /// Weak version of compare-and-exchange.
330    ///
331    /// This implementation currently delegates to the same lock-free CAS as
332    /// `compare_and_exchange`, so it does not introduce extra spurious
333    /// failures.
334    ///
335    /// # Parameters
336    ///
337    /// * `current` - The expected current reference.
338    /// * `new` - The new reference to set if current matches.
339    ///
340    /// # Returns
341    ///
342    /// The reference before the operation.
343    ///
344    /// # Example
345    ///
346    /// ```rust
347    /// use qubit_atomic::AtomicRef;
348    /// use std::sync::Arc;
349    ///
350    /// let atomic = AtomicRef::new(Arc::new(10));
351    /// let mut current = atomic.load();
352    /// loop {
353    ///     let prev =
354    ///         atomic.compare_and_exchange_weak(&current, Arc::new(20));
355    ///     if Arc::ptr_eq(&prev, &current) {
356    ///         break;
357    ///     }
358    ///     current = prev;
359    /// }
360    /// assert_eq!(*atomic.load(), 20);
361    /// ```
362    #[inline]
363    pub fn compare_and_exchange_weak(&self, current: &Arc<T>, new: Arc<T>) -> Arc<T> {
364        self.compare_and_exchange(current, new)
365    }
366
367    /// Updates the reference using a function, returning the old reference.
368    ///
369    /// Internally uses a CAS loop until the update succeeds.
370    ///
371    /// # Parameters
372    ///
373    /// * `f` - A function that takes the current reference and returns the
374    ///   new reference.
375    ///
376    /// # Returns
377    ///
378    /// The old reference before the update.
379    ///
380    /// # Example
381    ///
382    /// ```rust
383    /// use qubit_atomic::AtomicRef;
384    /// use std::sync::Arc;
385    ///
386    /// let atomic = AtomicRef::new(Arc::new(10));
387    /// let old = atomic.fetch_update(|x| Arc::new(**x * 2));
388    /// assert_eq!(*old, 10);
389    /// assert_eq!(*atomic.load(), 20);
390    /// ```
391    #[inline]
392    pub fn fetch_update<F>(&self, f: F) -> Arc<T>
393    where
394        F: Fn(&Arc<T>) -> Arc<T>,
395    {
396        let mut current = self.load();
397        loop {
398            let new = f(&current);
399            match self.compare_set_weak(&current, new) {
400                Ok(_) => return current,
401                Err(actual) => current = actual,
402            }
403        }
404    }
405
406    /// Gets a reference to the underlying `ArcSwap`.
407    ///
408    /// This allows advanced users to access lower-level `ArcSwap` APIs for
409    /// custom synchronization strategies.
410    ///
411    /// # Returns
412    ///
413    /// A reference to the underlying `arc_swap::ArcSwap<T>`.
414    #[inline]
415    pub fn inner(&self) -> &ArcSwap<T> {
416        &self.inner
417    }
418}
419
420impl<T> Clone for AtomicRef<T> {
421    /// Clones the atomic reference.
422    ///
423    /// Creates a new `AtomicRef` that initially points to the same value as
424    /// the original, but subsequent atomic operations are independent.
425    ///
426    /// # Returns
427    ///
428    /// A new atomic reference initialized with a clone of the currently loaded
429    /// `Arc`.
430    #[inline]
431    fn clone(&self) -> Self {
432        Self::new(self.load())
433    }
434}
435
436impl<T: fmt::Debug> fmt::Debug for AtomicRef<T> {
437    /// Formats the currently loaded reference for debugging.
438    ///
439    /// # Parameters
440    ///
441    /// * `f` - The formatter receiving the debug representation.
442    ///
443    /// # Returns
444    ///
445    /// A formatting result from the formatter.
446    #[inline]
447    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
448        f.debug_struct("AtomicRef")
449            .field("value", &self.load())
450            .finish()
451    }
452}
453
454impl<T: fmt::Display> fmt::Display for AtomicRef<T> {
455    /// Formats the currently loaded reference with display formatting.
456    ///
457    /// # Parameters
458    ///
459    /// * `f` - The formatter receiving the displayed value.
460    ///
461    /// # Returns
462    ///
463    /// A formatting result from the formatter.
464    #[inline]
465    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
466        write!(f, "{}", self.load())
467    }
468}