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(¤t, 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(¤t, Arc::new(20));
318 /// assert!(Arc::ptr_eq(&prev, ¤t));
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(¤t);
365 match self.compare_set(¤t, 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(¤t);
406 let returned = Arc::clone(&new);
407 match self.compare_set(¤t, 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(¤t)?;
458 match self.compare_set(¤t, 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(¤t)?;
510 let returned = Arc::clone(&new);
511 match self.compare_set(¤t, 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}