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    /// Compares and exchanges the reference atomically, returning the
273    /// previous reference.
274    ///
275    /// If the current reference equals `current` (by pointer equality), sets
276    /// it to `new` and returns the old reference. Otherwise, returns the
277    /// actual current reference.
278    ///
279    /// # Parameters
280    ///
281    /// * `current` - The expected current reference.
282    /// * `new` - The new reference to set if current matches.
283    ///
284    /// # Returns
285    ///
286    /// The reference observed before the operation completed. If it is pointer
287    /// equal to `current`, the exchange succeeded and `new` was stored.
288    /// Otherwise, it is the actual current reference that prevented the
289    /// exchange.
290    ///
291    /// # Note
292    ///
293    /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
294    ///
295    /// # Example
296    ///
297    /// ```rust
298    /// use qubit_atomic::AtomicRef;
299    /// use std::sync::Arc;
300    ///
301    /// let atomic = AtomicRef::new(Arc::new(10));
302    /// let current = atomic.load();
303    ///
304    /// let prev = atomic.compare_and_exchange(&current, Arc::new(20));
305    /// assert!(Arc::ptr_eq(&prev, &current));
306    /// assert_eq!(*atomic.load(), 20);
307    /// ```
308    #[inline]
309    pub fn compare_and_exchange(&self, current: &Arc<T>, new: Arc<T>) -> Arc<T> {
310        Guard::into_inner(self.inner.compare_and_swap(current, new))
311    }
312
313    /// Updates the reference using a function, returning the old reference.
314    ///
315    /// Internally uses a CAS loop until the update succeeds.
316    ///
317    /// # Parameters
318    ///
319    /// * `f` - A function that takes the current reference and returns the
320    ///   new reference.
321    ///
322    /// # Returns
323    ///
324    /// The old reference before the update.
325    ///
326    /// The closure may be called more than once when concurrent updates cause
327    /// CAS retries.
328    ///
329    /// # Example
330    ///
331    /// ```rust
332    /// use qubit_atomic::AtomicRef;
333    /// use std::sync::Arc;
334    ///
335    /// let atomic = AtomicRef::new(Arc::new(10));
336    /// let old = atomic.fetch_update(|x| Arc::new(**x * 2));
337    /// assert_eq!(*old, 10);
338    /// assert_eq!(*atomic.load(), 20);
339    /// ```
340    #[inline]
341    pub fn fetch_update<F>(&self, mut f: F) -> Arc<T>
342    where
343        F: FnMut(&Arc<T>) -> Arc<T>,
344    {
345        let mut current = self.load();
346        loop {
347            let new = f(&current);
348            match self.compare_set(&current, new) {
349                Ok(_) => return current,
350                Err(actual) => current = actual,
351            }
352        }
353    }
354
355    /// Updates the reference using a function, returning the new reference.
356    ///
357    /// Internally uses a CAS loop until the update succeeds.
358    ///
359    /// # Parameters
360    ///
361    /// * `f` - A function that takes the current reference and returns the
362    ///   new reference.
363    ///
364    /// # Returns
365    ///
366    /// The reference committed by the successful update.
367    ///
368    /// The closure may be called more than once when concurrent updates cause
369    /// CAS retries.
370    ///
371    /// # Example
372    ///
373    /// ```rust
374    /// use qubit_atomic::AtomicRef;
375    /// use std::sync::Arc;
376    ///
377    /// let atomic = AtomicRef::new(Arc::new(10));
378    /// let new = atomic.update_and_get(|x| Arc::new(**x * 2));
379    /// assert_eq!(*new, 20);
380    /// assert_eq!(*atomic.load(), 20);
381    /// ```
382    #[inline]
383    pub fn update_and_get<F>(&self, mut f: F) -> Arc<T>
384    where
385        F: FnMut(&Arc<T>) -> Arc<T>,
386    {
387        let mut current = self.load();
388        loop {
389            let new = f(&current);
390            let returned = Arc::clone(&new);
391            match self.compare_set(&current, new) {
392                Ok(_) => return returned,
393                Err(actual) => current = actual,
394            }
395        }
396    }
397
398    /// Conditionally updates the reference using a function.
399    ///
400    /// Internally uses a pointer-based CAS loop until the update succeeds or
401    /// the closure rejects the current reference by returning `None`.
402    ///
403    /// # Parameters
404    ///
405    /// * `f` - A function that takes the current reference and returns the new
406    ///   reference, or `None` to leave the current reference unchanged.
407    ///
408    /// # Returns
409    ///
410    /// `Some(old_reference)` when the update succeeds, or `None` when `f`
411    /// rejects the observed current reference.
412    ///
413    /// The closure may be called more than once when concurrent updates cause
414    /// CAS retries.
415    ///
416    /// # Example
417    ///
418    /// ```rust
419    /// use qubit_atomic::AtomicRef;
420    /// use std::sync::Arc;
421    ///
422    /// let atomic = AtomicRef::new(Arc::new(3));
423    /// let old = atomic.try_update(|current| {
424    ///     (**current % 2 == 1).then_some(Arc::new(**current + 1))
425    /// });
426    /// assert_eq!(*old.unwrap(), 3);
427    /// assert_eq!(*atomic.load(), 4);
428    /// assert!(atomic
429    ///     .try_update(|current| {
430    ///         (**current % 2 == 1).then_some(Arc::new(**current + 1))
431    ///     })
432    ///     .is_none());
433    /// assert_eq!(*atomic.load(), 4);
434    /// ```
435    #[inline]
436    pub fn try_update<F>(&self, mut f: F) -> Option<Arc<T>>
437    where
438        F: FnMut(&Arc<T>) -> Option<Arc<T>>,
439    {
440        let mut current = self.load();
441        loop {
442            let new = f(&current)?;
443            match self.compare_set(&current, new) {
444                Ok(_) => return Some(current),
445                Err(actual) => current = actual,
446            }
447        }
448    }
449
450    /// Conditionally updates the reference using a function, returning the new
451    /// reference.
452    ///
453    /// Internally uses a pointer-based CAS loop until the update succeeds or
454    /// the closure rejects the current reference by returning `None`.
455    ///
456    /// # Parameters
457    ///
458    /// * `f` - A function that takes the current reference and returns the new
459    ///   reference, or `None` to leave the current reference unchanged.
460    ///
461    /// # Returns
462    ///
463    /// `Some(new_reference)` when the update succeeds, or `None` when `f`
464    /// rejects the observed current reference.
465    ///
466    /// The closure may be called more than once when concurrent updates cause
467    /// CAS retries.
468    ///
469    /// # Example
470    ///
471    /// ```rust
472    /// use qubit_atomic::AtomicRef;
473    /// use std::sync::Arc;
474    ///
475    /// let atomic = AtomicRef::new(Arc::new(3));
476    /// let new = atomic.try_update_and_get(|current| {
477    ///     (**current % 2 == 1).then_some(Arc::new(**current + 1))
478    /// });
479    /// assert_eq!(*new.unwrap(), 4);
480    /// assert_eq!(*atomic.load(), 4);
481    /// assert!(atomic
482    ///     .try_update_and_get(|current| {
483    ///         (**current % 2 == 1).then_some(Arc::new(**current + 1))
484    ///     })
485    ///     .is_none());
486    /// assert_eq!(*atomic.load(), 4);
487    /// ```
488    #[inline]
489    pub fn try_update_and_get<F>(&self, mut f: F) -> Option<Arc<T>>
490    where
491        F: FnMut(&Arc<T>) -> Option<Arc<T>>,
492    {
493        let mut current = self.load();
494        loop {
495            let new = f(&current)?;
496            let returned = Arc::clone(&new);
497            match self.compare_set(&current, new) {
498                Ok(_) => return Some(returned),
499                Err(actual) => current = actual,
500            }
501        }
502    }
503
504    /// Gets a reference to the underlying `ArcSwap`.
505    ///
506    /// This allows advanced users to access lower-level `ArcSwap` APIs for
507    /// custom synchronization strategies.
508    ///
509    /// # Returns
510    ///
511    /// A reference to the underlying `arc_swap::ArcSwap<T>`.
512    #[inline]
513    pub fn inner(&self) -> &ArcSwap<T> {
514        &self.inner
515    }
516}
517
518impl<T> Clone for AtomicRef<T> {
519    /// Clones the atomic reference.
520    ///
521    /// Creates a new `AtomicRef` that initially points to the same value as
522    /// the original, but subsequent atomic operations are independent.
523    ///
524    /// # Returns
525    ///
526    /// A new atomic reference initialized with a clone of the currently loaded
527    /// `Arc`.
528    #[inline]
529    fn clone(&self) -> Self {
530        Self::new(self.load())
531    }
532}
533
534impl<T: fmt::Debug> fmt::Debug for AtomicRef<T> {
535    /// Formats the currently loaded reference for debugging.
536    ///
537    /// # Parameters
538    ///
539    /// * `f` - The formatter receiving the debug representation.
540    ///
541    /// # Returns
542    ///
543    /// A formatting result from the formatter.
544    #[inline]
545    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546        f.debug_struct("AtomicRef")
547            .field("value", &self.load())
548            .finish()
549    }
550}
551
552impl<T: fmt::Display> fmt::Display for AtomicRef<T> {
553    /// Formats the currently loaded reference with display formatting.
554    ///
555    /// # Parameters
556    ///
557    /// * `f` - The formatter receiving the displayed value.
558    ///
559    /// # Returns
560    ///
561    /// A formatting result from the formatter.
562    #[inline]
563    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
564        write!(f, "{}", self.load())
565    }
566}