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