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