qubit_atomic/atomic/atomic_ref.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # Atomic Reference
11//!
12//! Provides an easy-to-use atomic reference type with sensible default memory
13//! orderings. Uses `Arc<T>` for thread-safe reference counting.
14//!
15//! # Author
16//!
17//! Haixing Hu
18
19use arc_swap::ArcSwap;
20use std::fmt;
21use std::sync::Arc;
22
23use crate::atomic::traits::Atomic;
24
25/// Atomic reference type.
26///
27/// Provides easy-to-use atomic operations on references with automatic memory
28/// ordering selection. Uses `Arc<T>` for thread-safe reference counting.
29///
30/// # Implementation Details
31///
32/// This type is backed by `arc_swap::ArcSwap<T>`, which provides lock-free,
33/// memory-safe atomic replacement and loading of `Arc<T>` values without
34/// exposing raw-pointer lifetime hazards.
35///
36/// # Features
37///
38/// - Automatic memory ordering selection
39/// - Thread-safe reference counting via `Arc`
40/// - Functional update operations
41/// - Zero-cost abstraction with inline methods
42///
43/// # Example
44///
45/// ```rust
46/// use qubit_atomic::AtomicRef;
47/// use std::sync::Arc;
48///
49/// #[derive(Debug, Clone)]
50/// struct Config {
51/// timeout: u64,
52/// max_retries: u32,
53/// }
54///
55/// let config = Arc::new(Config {
56/// timeout: 1000,
57/// max_retries: 3,
58/// });
59///
60/// let atomic_config = AtomicRef::new(config);
61///
62/// // Update configuration
63/// let new_config = Arc::new(Config {
64/// timeout: 2000,
65/// max_retries: 5,
66/// });
67///
68/// let old_config = atomic_config.swap(new_config);
69/// assert_eq!(old_config.timeout, 1000);
70/// assert_eq!(atomic_config.load().timeout, 2000);
71/// ```
72///
73/// # Author
74///
75/// Haixing Hu
76pub struct AtomicRef<T> {
77 inner: ArcSwap<T>,
78}
79
80impl<T> AtomicRef<T> {
81 /// Creates a new atomic reference.
82 ///
83 /// # Parameters
84 ///
85 /// * `value` - The initial reference.
86 ///
87 /// # Example
88 ///
89 /// ```rust
90 /// use qubit_atomic::AtomicRef;
91 /// use std::sync::Arc;
92 ///
93 /// let data = Arc::new(42);
94 /// let atomic = AtomicRef::new(data);
95 /// assert_eq!(*atomic.load(), 42);
96 /// ```
97 #[inline]
98 pub fn new(value: Arc<T>) -> Self {
99 Self {
100 inner: ArcSwap::from(value),
101 }
102 }
103
104 /// Gets the current reference.
105 ///
106 /// # Returns
107 ///
108 /// A cloned `Arc` pointing to the current value.
109 ///
110 /// # Example
111 ///
112 /// ```rust
113 /// use qubit_atomic::AtomicRef;
114 /// use std::sync::Arc;
115 ///
116 /// let atomic = AtomicRef::new(Arc::new(42));
117 /// let value = atomic.load();
118 /// assert_eq!(*value, 42);
119 /// ```
120 #[inline]
121 pub fn load(&self) -> Arc<T> {
122 self.inner.load_full()
123 }
124
125 /// Sets a new reference.
126 ///
127 /// # Parameters
128 ///
129 /// * `value` - The new reference to set.
130 ///
131 /// # Example
132 ///
133 /// ```rust
134 /// use qubit_atomic::AtomicRef;
135 /// use std::sync::Arc;
136 ///
137 /// let atomic = AtomicRef::new(Arc::new(42));
138 /// atomic.store(Arc::new(100));
139 /// assert_eq!(*atomic.load(), 100);
140 /// ```
141 #[inline]
142 pub fn store(&self, value: Arc<T>) {
143 self.inner.store(value);
144 }
145
146 /// Swaps the current reference with a new reference, returning the old
147 /// reference.
148 ///
149 /// # Parameters
150 ///
151 /// * `value` - The new reference to swap in.
152 ///
153 /// # Returns
154 ///
155 /// The old reference.
156 ///
157 /// # Example
158 ///
159 /// ```rust
160 /// use qubit_atomic::AtomicRef;
161 /// use std::sync::Arc;
162 ///
163 /// let atomic = AtomicRef::new(Arc::new(10));
164 /// let old = atomic.swap(Arc::new(20));
165 /// assert_eq!(*old, 10);
166 /// assert_eq!(*atomic.load(), 20);
167 /// ```
168 #[inline]
169 pub fn swap(&self, value: Arc<T>) -> Arc<T> {
170 self.inner.swap(value)
171 }
172
173 /// Compares and sets the reference atomically.
174 ///
175 /// If the current reference equals `current` (by pointer equality), sets
176 /// it to `new` and returns `Ok(())`. Otherwise, returns `Err(actual)`
177 /// where `actual` is the current reference.
178 ///
179 /// # Parameters
180 ///
181 /// * `current` - The expected current reference.
182 /// * `new` - The new reference to set if current matches.
183 ///
184 /// # Returns
185 ///
186 /// `Ok(())` on success, or `Err(actual)` on failure.
187 ///
188 /// # Note
189 ///
190 /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
191 ///
192 /// # Example
193 ///
194 /// ```rust
195 /// use qubit_atomic::AtomicRef;
196 /// use std::sync::Arc;
197 ///
198 /// let atomic = AtomicRef::new(Arc::new(10));
199 /// let current = atomic.load();
200 ///
201 /// assert!(atomic.compare_set(¤t, Arc::new(20)).is_ok());
202 /// assert_eq!(*atomic.load(), 20);
203 /// ```
204 #[inline]
205 pub fn compare_set(&self, current: &Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
206 let prev = Arc::clone(&*self.inner.compare_and_swap(current, new));
207 if Arc::ptr_eq(&prev, current) {
208 Ok(())
209 } else {
210 Err(prev)
211 }
212 }
213
214 /// Weak version of compare-and-set.
215 ///
216 /// This implementation currently delegates to the same lock-free CAS as
217 /// `compare_set`, so it does not introduce extra spurious failures.
218 ///
219 /// # Parameters
220 ///
221 /// * `current` - The expected current reference.
222 /// * `new` - The new reference to set if current matches.
223 ///
224 /// # Returns
225 ///
226 /// `Ok(())` on success, or `Err(actual)` on failure.
227 ///
228 /// # Example
229 ///
230 /// ```rust
231 /// use qubit_atomic::AtomicRef;
232 /// use std::sync::Arc;
233 ///
234 /// let atomic = AtomicRef::new(Arc::new(10));
235 /// let mut current = atomic.load();
236 /// loop {
237 /// match atomic.compare_set_weak(¤t, Arc::new(20)) {
238 /// Ok(_) => break,
239 /// Err(actual) => current = actual,
240 /// }
241 /// }
242 /// assert_eq!(*atomic.load(), 20);
243 /// ```
244 #[inline]
245 pub fn compare_set_weak(&self, current: &Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
246 self.compare_set(current, new)
247 }
248
249 /// Compares and exchanges the reference atomically, returning the
250 /// previous reference.
251 ///
252 /// If the current reference equals `current` (by pointer equality), sets
253 /// it to `new` and returns the old reference. Otherwise, returns the
254 /// actual current reference.
255 ///
256 /// # Parameters
257 ///
258 /// * `current` - The expected current reference.
259 /// * `new` - The new reference to set if current matches.
260 ///
261 /// # Returns
262 ///
263 /// The reference before the operation.
264 ///
265 /// # Note
266 ///
267 /// Comparison uses pointer equality (`Arc::ptr_eq`), not value equality.
268 ///
269 /// # Example
270 ///
271 /// ```rust
272 /// use qubit_atomic::AtomicRef;
273 /// use std::sync::Arc;
274 ///
275 /// let atomic = AtomicRef::new(Arc::new(10));
276 /// let current = atomic.load();
277 ///
278 /// let prev = atomic.compare_and_exchange(¤t, Arc::new(20));
279 /// assert!(Arc::ptr_eq(&prev, ¤t));
280 /// assert_eq!(*atomic.load(), 20);
281 /// ```
282 #[inline]
283 pub fn compare_and_exchange(&self, current: &Arc<T>, new: Arc<T>) -> Arc<T> {
284 Arc::clone(&*self.inner.compare_and_swap(current, new))
285 }
286
287 /// Weak version of compare-and-exchange.
288 ///
289 /// This implementation currently delegates to the same lock-free CAS as
290 /// `compare_and_exchange`, so it does not introduce extra spurious
291 /// failures.
292 ///
293 /// # Parameters
294 ///
295 /// * `current` - The expected current reference.
296 /// * `new` - The new reference to set if current matches.
297 ///
298 /// # Returns
299 ///
300 /// The reference before the operation.
301 ///
302 /// # Example
303 ///
304 /// ```rust
305 /// use qubit_atomic::AtomicRef;
306 /// use std::sync::Arc;
307 ///
308 /// let atomic = AtomicRef::new(Arc::new(10));
309 /// let mut current = atomic.load();
310 /// loop {
311 /// let prev =
312 /// atomic.compare_and_exchange_weak(¤t, Arc::new(20));
313 /// if Arc::ptr_eq(&prev, ¤t) {
314 /// break;
315 /// }
316 /// current = prev;
317 /// }
318 /// assert_eq!(*atomic.load(), 20);
319 /// ```
320 #[inline]
321 pub fn compare_and_exchange_weak(&self, current: &Arc<T>, new: Arc<T>) -> Arc<T> {
322 self.compare_and_exchange(current, new)
323 }
324
325 /// Updates the reference using a function, returning the old reference.
326 ///
327 /// Internally uses a CAS loop until the update succeeds.
328 ///
329 /// # Parameters
330 ///
331 /// * `f` - A function that takes the current reference and returns the
332 /// new reference.
333 ///
334 /// # Returns
335 ///
336 /// The old reference before the update.
337 ///
338 /// # Example
339 ///
340 /// ```rust
341 /// use qubit_atomic::AtomicRef;
342 /// use std::sync::Arc;
343 ///
344 /// let atomic = AtomicRef::new(Arc::new(10));
345 /// let old = atomic.fetch_update(|x| Arc::new(**x * 2));
346 /// assert_eq!(*old, 10);
347 /// assert_eq!(*atomic.load(), 20);
348 /// ```
349 #[inline]
350 pub fn fetch_update<F>(&self, f: F) -> Arc<T>
351 where
352 F: Fn(&Arc<T>) -> Arc<T>,
353 {
354 let mut current = self.load();
355 loop {
356 let new = f(¤t);
357 match self.compare_set_weak(¤t, new) {
358 Ok(_) => return current,
359 Err(actual) => current = actual,
360 }
361 }
362 }
363
364 /// Gets a reference to the underlying `ArcSwap`.
365 ///
366 /// This allows advanced users to access lower-level `ArcSwap` APIs for
367 /// custom synchronization strategies.
368 ///
369 /// # Returns
370 ///
371 /// A reference to the underlying `arc_swap::ArcSwap<T>`.
372 #[inline]
373 pub fn inner(&self) -> &ArcSwap<T> {
374 &self.inner
375 }
376}
377
378impl<T> Atomic for AtomicRef<T> {
379 type Value = Arc<T>;
380
381 #[inline]
382 fn load(&self) -> Arc<T> {
383 self.load()
384 }
385
386 #[inline]
387 fn store(&self, value: Arc<T>) {
388 self.store(value);
389 }
390
391 #[inline]
392 fn swap(&self, value: Arc<T>) -> Arc<T> {
393 self.swap(value)
394 }
395
396 #[inline]
397 fn compare_set(&self, current: Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
398 self.compare_set(¤t, new)
399 }
400
401 #[inline]
402 fn compare_set_weak(&self, current: Arc<T>, new: Arc<T>) -> Result<(), Arc<T>> {
403 self.compare_set_weak(¤t, new)
404 }
405
406 #[inline]
407 fn compare_exchange(&self, current: Arc<T>, new: Arc<T>) -> Arc<T> {
408 self.compare_and_exchange(¤t, new)
409 }
410
411 #[inline]
412 fn compare_exchange_weak(&self, current: Arc<T>, new: Arc<T>) -> Arc<T> {
413 self.compare_and_exchange_weak(¤t, new)
414 }
415
416 #[inline]
417 fn fetch_update<F>(&self, f: F) -> Arc<T>
418 where
419 F: Fn(Arc<T>) -> Arc<T>,
420 {
421 self.fetch_update(|x| f(x.clone()))
422 }
423}
424
425impl<T> Clone for AtomicRef<T> {
426 /// Clones the atomic reference.
427 ///
428 /// Creates a new `AtomicRef` that initially points to the same value as
429 /// the original, but subsequent atomic operations are independent.
430 #[inline]
431 fn clone(&self) -> Self {
432 Self::new(self.load())
433 }
434}
435
436impl<T: fmt::Debug> fmt::Debug for AtomicRef<T> {
437 #[inline]
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 f.debug_struct("AtomicRef")
440 .field("value", &self.load())
441 .finish()
442 }
443}
444
445impl<T: fmt::Display> fmt::Display for AtomicRef<T> {
446 #[inline]
447 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
448 write!(f, "{}", self.load())
449 }
450}