Skip to main content

qubit_atomic/atomic/
atomic_ref.rs

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