Skip to main content

qubit_atomic/atomic/
atomic_ref.rs

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