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    /// 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(&current, 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(&current, 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(&current, Arc::new(20));
294    /// assert!(Arc::ptr_eq(&prev, &current));
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(&current, Arc::new(20));
328    ///     if Arc::ptr_eq(&prev, &current) {
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(&current);
372            match self.compare_set_weak(&current, 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}