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/// # Example
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    /// # Example
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    /// # Example
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]
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    /// # Example
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    #[inline]
156    pub fn load(&self) -> Arc<T> {
157        self.inner.load_full()
158    }
159
160    /// Gets the current reference as an `ArcSwap` guard.
161    ///
162    /// This is useful for short-lived reads because it avoids cloning the
163    /// underlying [`Arc`] on the fast path. Use [`load`](Self::load) when the
164    /// caller needs an owned [`Arc<T>`] that can be stored or moved freely.
165    ///
166    /// # Returns
167    ///
168    /// A guard pointing to the current `Arc`.
169    ///
170    /// # Example
171    ///
172    /// ```rust
173    /// use qubit_atomic::AtomicRef;
174    /// use std::sync::Arc;
175    ///
176    /// let atomic = AtomicRef::new(Arc::new(42));
177    /// let guard = atomic.load_guard();
178    /// assert_eq!(**guard, 42);
179    /// ```
180    #[inline]
181    pub fn load_guard(&self) -> Guard<Arc<T>> {
182        self.inner.load()
183    }
184
185    /// Sets a new reference.
186    ///
187    /// # Parameters
188    ///
189    /// * `value` - The new reference to set.
190    ///
191    /// # Example
192    ///
193    /// ```rust
194    /// use qubit_atomic::AtomicRef;
195    /// use std::sync::Arc;
196    ///
197    /// let atomic = AtomicRef::new(Arc::new(42));
198    /// atomic.store(Arc::new(100));
199    /// assert_eq!(*atomic.load(), 100);
200    /// ```
201    #[inline]
202    pub fn store(&self, value: Arc<T>) {
203        self.inner.store(value);
204    }
205
206    /// Swaps the current reference with a new reference, returning the old
207    /// reference.
208    ///
209    /// # Parameters
210    ///
211    /// * `value` - The new reference to swap in.
212    ///
213    /// # Returns
214    ///
215    /// The old reference.
216    ///
217    /// # Example
218    ///
219    /// ```rust
220    /// use qubit_atomic::AtomicRef;
221    /// use std::sync::Arc;
222    ///
223    /// let atomic = AtomicRef::new(Arc::new(10));
224    /// let old = atomic.swap(Arc::new(20));
225    /// assert_eq!(*old, 10);
226    /// assert_eq!(*atomic.load(), 20);
227    /// ```
228    #[inline]
229    pub fn swap(&self, value: Arc<T>) -> Arc<T> {
230        self.inner.swap(value)
231    }
232
233    /// Compares and sets the reference atomically.
234    ///
235    /// If the current reference equals `current` (by pointer equality), sets
236    /// it to `new` and returns `Ok(())`. Otherwise, returns `Err(actual)`
237    /// where `actual` is the current reference.
238    ///
239    /// # Parameters
240    ///
241    /// * `current` - The expected current reference.
242    /// * `new` - The new reference to set if current matches.
243    ///
244    /// # Returns
245    ///
246    /// `Ok(())` if the pointer comparison succeeds and `new` is stored.
247    ///
248    /// # Errors
249    ///
250    /// Returns `Err(actual)` with the observed current reference when the
251    /// pointer comparison fails. On failure, `new` is not installed.
252    ///
253    /// # Note
254    ///
255    /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
256    ///
257    /// # Example
258    ///
259    /// ```rust
260    /// use qubit_atomic::AtomicRef;
261    /// use std::sync::Arc;
262    ///
263    /// let atomic = AtomicRef::new(Arc::new(10));
264    /// let current = atomic.load();
265    ///
266    /// assert!(atomic.compare_set(&current, Arc::new(20)).is_ok());
267    /// assert_eq!(*atomic.load(), 20);
268    /// ```
269    #[inline]
270    pub fn compare_set(
271        &self,
272        current: &Arc<T>,
273        new: Arc<T>,
274    ) -> Result<(), Arc<T>> {
275        let prev = Guard::into_inner(self.inner.compare_and_swap(current, new));
276        if Arc::ptr_eq(&prev, current) {
277            Ok(())
278        } else {
279            Err(prev)
280        }
281    }
282
283    /// Compares and exchanges the reference atomically, returning the
284    /// previous reference.
285    ///
286    /// If the current reference equals `current` (by pointer equality), sets
287    /// it to `new` and returns the old reference. Otherwise, returns the
288    /// actual current reference.
289    ///
290    /// # Parameters
291    ///
292    /// * `current` - The expected current reference.
293    /// * `new` - The new reference to set if current matches.
294    ///
295    /// # Returns
296    ///
297    /// The reference observed before the operation completed. If it is pointer
298    /// equal to `current`, the exchange succeeded and `new` was stored.
299    /// Otherwise, it is the actual current reference that prevented the
300    /// exchange.
301    ///
302    /// # Note
303    ///
304    /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
305    ///
306    /// # Example
307    ///
308    /// ```rust
309    /// use qubit_atomic::AtomicRef;
310    /// use std::sync::Arc;
311    ///
312    /// let atomic = AtomicRef::new(Arc::new(10));
313    /// let current = atomic.load();
314    ///
315    /// let prev = atomic.compare_and_exchange(&current, Arc::new(20));
316    /// assert!(Arc::ptr_eq(&prev, &current));
317    /// assert_eq!(*atomic.load(), 20);
318    /// ```
319    #[inline]
320    pub fn compare_and_exchange(
321        &self,
322        current: &Arc<T>,
323        new: Arc<T>,
324    ) -> Arc<T> {
325        Guard::into_inner(self.inner.compare_and_swap(current, new))
326    }
327
328    /// Updates the reference using a function, returning the old reference.
329    ///
330    /// Internally uses a CAS loop until the update succeeds.
331    ///
332    /// # Parameters
333    ///
334    /// * `f` - A function that takes the current reference and returns the new
335    ///   reference.
336    ///
337    /// # Returns
338    ///
339    /// The old reference before the update.
340    ///
341    /// The closure may be called more than once when concurrent updates cause
342    /// CAS retries.
343    ///
344    /// # Example
345    ///
346    /// ```rust
347    /// use qubit_atomic::AtomicRef;
348    /// use std::sync::Arc;
349    ///
350    /// let atomic = AtomicRef::new(Arc::new(10));
351    /// let old = atomic.fetch_update(|x| Arc::new(**x * 2));
352    /// assert_eq!(*old, 10);
353    /// assert_eq!(*atomic.load(), 20);
354    /// ```
355    #[inline]
356    pub fn fetch_update<F>(&self, mut f: F) -> Arc<T>
357    where
358        F: FnMut(&Arc<T>) -> Arc<T>,
359    {
360        let mut current = self.load();
361        loop {
362            let new = f(&current);
363            match self.compare_set(&current, new) {
364                Ok(_) => return current,
365                Err(actual) => current = actual,
366            }
367        }
368    }
369
370    /// Updates the reference using a function, returning the new reference.
371    ///
372    /// Internally uses a CAS loop until the update succeeds.
373    ///
374    /// # Parameters
375    ///
376    /// * `f` - A function that takes the current reference and returns the new
377    ///   reference.
378    ///
379    /// # Returns
380    ///
381    /// The reference committed by the successful update.
382    ///
383    /// The closure may be called more than once when concurrent updates cause
384    /// CAS retries.
385    ///
386    /// # Example
387    ///
388    /// ```rust
389    /// use qubit_atomic::AtomicRef;
390    /// use std::sync::Arc;
391    ///
392    /// let atomic = AtomicRef::new(Arc::new(10));
393    /// let new = atomic.update_and_get(|x| Arc::new(**x * 2));
394    /// assert_eq!(*new, 20);
395    /// assert_eq!(*atomic.load(), 20);
396    /// ```
397    #[inline]
398    pub fn update_and_get<F>(&self, mut f: F) -> Arc<T>
399    where
400        F: FnMut(&Arc<T>) -> Arc<T>,
401    {
402        let mut current = self.load();
403        loop {
404            let new = f(&current);
405            let returned = Arc::clone(&new);
406            match self.compare_set(&current, new) {
407                Ok(_) => return returned,
408                Err(actual) => current = actual,
409            }
410        }
411    }
412
413    /// Conditionally updates the reference using a function.
414    ///
415    /// Internally uses a pointer-based CAS loop until the update succeeds or
416    /// the closure rejects the current reference by returning `None`.
417    ///
418    /// # Parameters
419    ///
420    /// * `f` - A function that takes the current reference and returns the new
421    ///   reference, or `None` to leave the current reference unchanged.
422    ///
423    /// # Returns
424    ///
425    /// `Some(old_reference)` when the update succeeds, or `None` when `f`
426    /// rejects the observed current reference.
427    ///
428    /// The closure may be called more than once when concurrent updates cause
429    /// CAS retries.
430    ///
431    /// # Example
432    ///
433    /// ```rust
434    /// use qubit_atomic::AtomicRef;
435    /// use std::sync::Arc;
436    ///
437    /// let atomic = AtomicRef::new(Arc::new(3));
438    /// let old = atomic.try_update(|current| {
439    ///     (**current % 2 == 1).then_some(Arc::new(**current + 1))
440    /// });
441    /// assert_eq!(*old.unwrap(), 3);
442    /// assert_eq!(*atomic.load(), 4);
443    /// assert!(atomic
444    ///     .try_update(|current| {
445    ///         (**current % 2 == 1).then_some(Arc::new(**current + 1))
446    ///     })
447    ///     .is_none());
448    /// assert_eq!(*atomic.load(), 4);
449    /// ```
450    #[inline]
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    /// # Example
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    #[inline]
504    pub fn try_update_and_get<F>(&self, mut f: F) -> Option<Arc<T>>
505    where
506        F: FnMut(&Arc<T>) -> Option<Arc<T>>,
507    {
508        let mut current = self.load();
509        loop {
510            let new = f(&current)?;
511            let returned = Arc::clone(&new);
512            match self.compare_set(&current, new) {
513                Ok(_) => return Some(returned),
514                Err(actual) => current = actual,
515            }
516        }
517    }
518
519    /// Gets a reference to the underlying `ArcSwap`.
520    ///
521    /// This allows advanced users to access lower-level `ArcSwap` APIs for
522    /// custom synchronization strategies.
523    ///
524    /// # Returns
525    ///
526    /// A reference to the underlying `arc_swap::ArcSwap<T>`.
527    #[inline]
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    /// # Example
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]
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}