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(¤t, 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(¤t, Arc::new(20));
316 /// assert!(Arc::ptr_eq(&prev, ¤t));
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(¤t);
363 match self.compare_set(¤t, 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(¤t);
405 let returned = Arc::clone(&new);
406 match self.compare_set(¤t, 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(¤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 /// # 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(¤t)?;
511 let returned = Arc::clone(&new);
512 match self.compare_set(¤t, 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}