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::{
20    ArcSwap,
21    Guard,
22};
23use std::fmt;
24use std::sync::Arc;
25
26/// Atomic reference type.
27///
28/// Provides easy-to-use atomic operations on references with automatic memory
29/// ordering selection. Uses `Arc<T>` for thread-safe reference counting.
30///
31/// # Implementation Details
32///
33/// This type is backed by `arc_swap::ArcSwap<T>`, which provides lock-free,
34/// memory-safe atomic replacement and loading of `Arc<T>` values without
35/// exposing raw-pointer lifetime hazards.
36///
37/// # Features
38///
39/// - Automatic memory ordering selection
40/// - Thread-safe reference counting via `Arc`
41/// - Functional update operations
42/// - Zero-cost abstraction with inline methods
43///
44/// # Example
45///
46/// ```rust
47/// use qubit_atomic::AtomicRef;
48/// use std::sync::Arc;
49///
50/// #[derive(Debug, Clone)]
51/// struct Config {
52///     timeout: u64,
53///     max_retries: u32,
54/// }
55///
56/// let config = Arc::new(Config {
57///     timeout: 1000,
58///     max_retries: 3,
59/// });
60///
61/// let atomic_config = AtomicRef::new(config);
62///
63/// // Update configuration
64/// let new_config = Arc::new(Config {
65///     timeout: 2000,
66///     max_retries: 5,
67/// });
68///
69/// let old_config = atomic_config.swap(new_config);
70/// assert_eq!(old_config.timeout, 1000);
71/// assert_eq!(atomic_config.load().timeout, 2000);
72/// ```
73///
74/// # Author
75///
76/// Haixing Hu
77pub struct AtomicRef<T> {
78    /// Lock-free atomic storage for the current shared reference.
79    inner: ArcSwap<T>,
80}
81
82impl<T> AtomicRef<T> {
83    /// Creates a new atomic reference.
84    ///
85    /// # Parameters
86    ///
87    /// * `value` - The initial reference.
88    ///
89    /// # Returns
90    ///
91    /// An atomic reference initialized to `value`.
92    ///
93    /// # Example
94    ///
95    /// ```rust
96    /// use qubit_atomic::AtomicRef;
97    /// use std::sync::Arc;
98    ///
99    /// let data = Arc::new(42);
100    /// let atomic = AtomicRef::new(data);
101    /// assert_eq!(*atomic.load(), 42);
102    /// ```
103    #[inline]
104    pub fn new(value: Arc<T>) -> Self {
105        Self {
106            inner: ArcSwap::from(value),
107        }
108    }
109
110    /// Creates a new atomic reference from an owned value.
111    ///
112    /// This is a convenience constructor for callers that do not already have
113    /// an [`Arc<T>`]. It wraps `value` in [`Arc::new`] and then delegates to
114    /// [`new`](Self::new).
115    ///
116    /// # Parameters
117    ///
118    /// * `value` - The owned value to store as the initial reference.
119    ///
120    /// # Returns
121    ///
122    /// An atomic reference initialized to `Arc::new(value)`.
123    ///
124    /// # Example
125    ///
126    /// ```rust
127    /// use qubit_atomic::AtomicRef;
128    ///
129    /// let atomic = AtomicRef::from_value(42);
130    /// assert_eq!(*atomic.load(), 42);
131    /// ```
132    #[inline]
133    pub fn from_value(value: T) -> Self {
134        Self::new(Arc::new(value))
135    }
136
137    /// Gets the current reference.
138    ///
139    /// # Returns
140    ///
141    /// A cloned `Arc` pointing to the current value.
142    ///
143    /// # Example
144    ///
145    /// ```rust
146    /// use qubit_atomic::AtomicRef;
147    /// use std::sync::Arc;
148    ///
149    /// let atomic = AtomicRef::new(Arc::new(42));
150    /// let value = atomic.load();
151    /// assert_eq!(*value, 42);
152    /// ```
153    #[inline]
154    pub fn load(&self) -> Arc<T> {
155        self.inner.load_full()
156    }
157
158    /// Gets the current reference as an `ArcSwap` guard.
159    ///
160    /// This is useful for short-lived reads because it avoids cloning the
161    /// underlying [`Arc`] on the fast path. Use [`load`](Self::load) when the
162    /// caller needs an owned [`Arc<T>`] that can be stored or moved freely.
163    ///
164    /// # Returns
165    ///
166    /// A guard pointing to the current `Arc`.
167    ///
168    /// # Example
169    ///
170    /// ```rust
171    /// use qubit_atomic::AtomicRef;
172    /// use std::sync::Arc;
173    ///
174    /// let atomic = AtomicRef::new(Arc::new(42));
175    /// let guard = atomic.load_guard();
176    /// assert_eq!(**guard, 42);
177    /// ```
178    #[inline]
179    pub fn load_guard(&self) -> Guard<Arc<T>> {
180        self.inner.load()
181    }
182
183    /// Sets a new reference.
184    ///
185    /// # Parameters
186    ///
187    /// * `value` - The new reference to set.
188    ///
189    /// # Example
190    ///
191    /// ```rust
192    /// use qubit_atomic::AtomicRef;
193    /// use std::sync::Arc;
194    ///
195    /// let atomic = AtomicRef::new(Arc::new(42));
196    /// atomic.store(Arc::new(100));
197    /// assert_eq!(*atomic.load(), 100);
198    /// ```
199    #[inline]
200    pub fn store(&self, value: Arc<T>) {
201        self.inner.store(value);
202    }
203
204    /// Swaps the current reference with a new reference, returning the old
205    /// reference.
206    ///
207    /// # Parameters
208    ///
209    /// * `value` - The new reference to swap in.
210    ///
211    /// # Returns
212    ///
213    /// The old reference.
214    ///
215    /// # Example
216    ///
217    /// ```rust
218    /// use qubit_atomic::AtomicRef;
219    /// use std::sync::Arc;
220    ///
221    /// let atomic = AtomicRef::new(Arc::new(10));
222    /// let old = atomic.swap(Arc::new(20));
223    /// assert_eq!(*old, 10);
224    /// assert_eq!(*atomic.load(), 20);
225    /// ```
226    #[inline]
227    pub fn swap(&self, value: Arc<T>) -> Arc<T> {
228        self.inner.swap(value)
229    }
230
231    /// Compares and sets the reference atomically.
232    ///
233    /// If the current reference equals `current` (by pointer equality), sets
234    /// it to `new` and returns `Ok(())`. Otherwise, returns `Err(actual)`
235    /// where `actual` is the current reference.
236    ///
237    /// # Parameters
238    ///
239    /// * `current` - The expected current reference.
240    /// * `new` - The new reference to set if current matches.
241    ///
242    /// # Returns
243    ///
244    /// `Ok(())` if the pointer comparison succeeds and `new` is stored.
245    ///
246    /// # Errors
247    ///
248    /// Returns `Err(actual)` with the observed current reference when the
249    /// pointer comparison fails. On failure, `new` is not installed.
250    ///
251    /// # Note
252    ///
253    /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
254    ///
255    /// # Example
256    ///
257    /// ```rust
258    /// use qubit_atomic::AtomicRef;
259    /// use std::sync::Arc;
260    ///
261    /// let atomic = AtomicRef::new(Arc::new(10));
262    /// let current = atomic.load();
263    ///
264    /// assert!(atomic.compare_set(&current, Arc::new(20)).is_ok());
265    /// assert_eq!(*atomic.load(), 20);
266    /// ```
267    #[inline]
268    pub fn compare_set(&self, current: &Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
269        let prev = Guard::into_inner(self.inner.compare_and_swap(current, new));
270        if Arc::ptr_eq(&prev, current) {
271            Ok(())
272        } else {
273            Err(prev)
274        }
275    }
276
277    /// Weak version of compare-and-set.
278    ///
279    /// This implementation currently delegates to the same lock-free CAS as
280    /// `compare_set`, so it does not introduce extra spurious failures.
281    ///
282    /// # Parameters
283    ///
284    /// * `current` - The expected current reference.
285    /// * `new` - The new reference to set if current matches.
286    ///
287    /// # Returns
288    ///
289    /// `Ok(())` if the pointer comparison succeeds and `new` is stored.
290    ///
291    /// # Errors
292    ///
293    /// Returns `Err(actual)` with the observed current reference when the
294    /// pointer comparison fails. On failure, `new` is not installed. This
295    /// implementation currently delegates to [`compare_set`](Self::compare_set)
296    /// and does not add extra spurious failures.
297    ///
298    /// # Example
299    ///
300    /// ```rust
301    /// use qubit_atomic::AtomicRef;
302    /// use std::sync::Arc;
303    ///
304    /// let atomic = AtomicRef::new(Arc::new(10));
305    /// let mut current = atomic.load();
306    /// loop {
307    ///     match atomic.compare_set_weak(&current, Arc::new(20)) {
308    ///         Ok(_) => break,
309    ///         Err(actual) => current = actual,
310    ///     }
311    /// }
312    /// assert_eq!(*atomic.load(), 20);
313    /// ```
314    #[inline]
315    pub fn compare_set_weak(&self, current: &Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
316        self.compare_set(current, new)
317    }
318
319    /// Compares and exchanges the reference atomically, returning the
320    /// previous reference.
321    ///
322    /// If the current reference equals `current` (by pointer equality), sets
323    /// it to `new` and returns the old reference. Otherwise, returns the
324    /// actual current reference.
325    ///
326    /// # Parameters
327    ///
328    /// * `current` - The expected current reference.
329    /// * `new` - The new reference to set if current matches.
330    ///
331    /// # Returns
332    ///
333    /// The reference before the operation.
334    ///
335    /// # Note
336    ///
337    /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
338    ///
339    /// # Example
340    ///
341    /// ```rust
342    /// use qubit_atomic::AtomicRef;
343    /// use std::sync::Arc;
344    ///
345    /// let atomic = AtomicRef::new(Arc::new(10));
346    /// let current = atomic.load();
347    ///
348    /// let prev = atomic.compare_and_exchange(&current, Arc::new(20));
349    /// assert!(Arc::ptr_eq(&prev, &current));
350    /// assert_eq!(*atomic.load(), 20);
351    /// ```
352    #[inline]
353    pub fn compare_and_exchange(&self, current: &Arc<T>, new: Arc<T>) -> Arc<T> {
354        Guard::into_inner(self.inner.compare_and_swap(current, new))
355    }
356
357    /// Weak version of compare-and-exchange.
358    ///
359    /// This implementation currently delegates to the same lock-free CAS as
360    /// `compare_and_exchange`, so it does not introduce extra spurious
361    /// failures.
362    ///
363    /// # Parameters
364    ///
365    /// * `current` - The expected current reference.
366    /// * `new` - The new reference to set if current matches.
367    ///
368    /// # Returns
369    ///
370    /// The reference before the operation.
371    ///
372    /// # Example
373    ///
374    /// ```rust
375    /// use qubit_atomic::AtomicRef;
376    /// use std::sync::Arc;
377    ///
378    /// let atomic = AtomicRef::new(Arc::new(10));
379    /// let mut current = atomic.load();
380    /// loop {
381    ///     let prev =
382    ///         atomic.compare_and_exchange_weak(&current, Arc::new(20));
383    ///     if Arc::ptr_eq(&prev, &current) {
384    ///         break;
385    ///     }
386    ///     current = prev;
387    /// }
388    /// assert_eq!(*atomic.load(), 20);
389    /// ```
390    #[inline]
391    pub fn compare_and_exchange_weak(&self, current: &Arc<T>, new: Arc<T>) -> Arc<T> {
392        self.compare_and_exchange(current, new)
393    }
394
395    /// Updates the reference using a function, returning the old reference.
396    ///
397    /// Internally uses a CAS loop until the update succeeds.
398    ///
399    /// # Parameters
400    ///
401    /// * `f` - A function that takes the current reference and returns the
402    ///   new reference.
403    ///
404    /// # Returns
405    ///
406    /// The old reference before the update.
407    ///
408    /// The closure may be called more than once when concurrent updates cause
409    /// CAS retries.
410    ///
411    /// # Example
412    ///
413    /// ```rust
414    /// use qubit_atomic::AtomicRef;
415    /// use std::sync::Arc;
416    ///
417    /// let atomic = AtomicRef::new(Arc::new(10));
418    /// let old = atomic.fetch_update(|x| Arc::new(**x * 2));
419    /// assert_eq!(*old, 10);
420    /// assert_eq!(*atomic.load(), 20);
421    /// ```
422    #[inline]
423    pub fn fetch_update<F>(&self, f: F) -> Arc<T>
424    where
425        F: Fn(&Arc<T>) -> Arc<T>,
426    {
427        let mut current = self.load();
428        loop {
429            let new = f(&current);
430            match self.compare_set_weak(&current, new) {
431                Ok(_) => return current,
432                Err(actual) => current = actual,
433            }
434        }
435    }
436
437    /// Conditionally updates the reference using a function.
438    ///
439    /// Internally uses a pointer-based CAS loop until the update succeeds or
440    /// the closure rejects the current reference by returning `None`.
441    ///
442    /// # Parameters
443    ///
444    /// * `f` - A function that takes the current reference and returns the new
445    ///   reference, or `None` to leave the current reference unchanged.
446    ///
447    /// # Returns
448    ///
449    /// `Some(old_reference)` when the update succeeds, or `None` when `f`
450    /// rejects the observed current reference.
451    ///
452    /// The closure may be called more than once when concurrent updates cause
453    /// CAS retries.
454    ///
455    /// # Example
456    ///
457    /// ```rust
458    /// use qubit_atomic::AtomicRef;
459    /// use std::sync::Arc;
460    ///
461    /// let atomic = AtomicRef::new(Arc::new(3));
462    /// let old = atomic.try_update(|current| {
463    ///     (**current % 2 == 1).then_some(Arc::new(**current + 1))
464    /// });
465    /// assert_eq!(*old.unwrap(), 3);
466    /// assert_eq!(*atomic.load(), 4);
467    /// assert!(atomic
468    ///     .try_update(|current| {
469    ///         (**current % 2 == 1).then_some(Arc::new(**current + 1))
470    ///     })
471    ///     .is_none());
472    /// assert_eq!(*atomic.load(), 4);
473    /// ```
474    #[inline]
475    pub fn try_update<F>(&self, f: F) -> Option<Arc<T>>
476    where
477        F: Fn(&Arc<T>) -> Option<Arc<T>>,
478    {
479        let mut current = self.load();
480        loop {
481            let new = f(&current)?;
482            match self.compare_set_weak(&current, new) {
483                Ok(_) => return Some(current),
484                Err(actual) => current = actual,
485            }
486        }
487    }
488
489    /// Gets a reference to the underlying `ArcSwap`.
490    ///
491    /// This allows advanced users to access lower-level `ArcSwap` APIs for
492    /// custom synchronization strategies.
493    ///
494    /// # Returns
495    ///
496    /// A reference to the underlying `arc_swap::ArcSwap<T>`.
497    #[inline]
498    pub fn inner(&self) -> &ArcSwap<T> {
499        &self.inner
500    }
501}
502
503impl<T> Clone for AtomicRef<T> {
504    /// Clones the atomic reference.
505    ///
506    /// Creates a new `AtomicRef` that initially points to the same value as
507    /// the original, but subsequent atomic operations are independent.
508    ///
509    /// # Returns
510    ///
511    /// A new atomic reference initialized with a clone of the currently loaded
512    /// `Arc`.
513    #[inline]
514    fn clone(&self) -> Self {
515        Self::new(self.load())
516    }
517}
518
519impl<T: fmt::Debug> fmt::Debug for AtomicRef<T> {
520    /// Formats the currently loaded reference for debugging.
521    ///
522    /// # Parameters
523    ///
524    /// * `f` - The formatter receiving the debug representation.
525    ///
526    /// # Returns
527    ///
528    /// A formatting result from the formatter.
529    #[inline]
530    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531        f.debug_struct("AtomicRef")
532            .field("value", &self.load())
533            .finish()
534    }
535}
536
537impl<T: fmt::Display> fmt::Display for AtomicRef<T> {
538    /// Formats the currently loaded reference with display formatting.
539    ///
540    /// # Parameters
541    ///
542    /// * `f` - The formatter receiving the displayed value.
543    ///
544    /// # Returns
545    ///
546    /// A formatting result from the formatter.
547    #[inline]
548    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549        write!(f, "{}", self.load())
550    }
551}