prism3_atomic/atomic/
atomic_f64.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025.
4 *    3-Prism Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # Atomic 64-bit Floating Point
11//!
12//! Provides an easy-to-use atomic 64-bit floating point type with sensible
13//! default memory orderings. Implemented using bit conversion with AtomicU64.
14//!
15//! # Author
16//!
17//! Haixing Hu
18
19use std::fmt;
20use std::sync::atomic::AtomicU64;
21use std::sync::atomic::Ordering;
22
23use crate::atomic::traits::Atomic;
24use crate::atomic::traits::AtomicNumber;
25
26/// Atomic 64-bit floating point number.
27///
28/// Provides easy-to-use atomic operations with automatic memory ordering
29/// selection. Implemented using `AtomicU64` with bit conversion.
30///
31/// # Memory Ordering Strategy
32///
33/// This type uses the same memory ordering strategy as atomic integers:
34///
35/// - **Read operations** (`load`): Use `Acquire` ordering to ensure
36///   visibility of prior writes from other threads.
37///
38/// - **Write operations** (`store`): Use `Release` ordering to ensure
39///   visibility of prior writes to other threads.
40///
41/// - **Read-Modify-Write operations** (`swap`, `compare_set`): Use
42///   `AcqRel` ordering for full synchronization.
43///
44/// - **CAS-based arithmetic** (`fetch_add`, `fetch_sub`, etc.): Use
45///   `AcqRel` on success and `Acquire` on failure within the CAS loop.
46///   The loop ensures eventual consistency.
47///
48/// # Implementation Details
49///
50/// Since hardware doesn't provide native atomic floating-point operations,
51/// this type is implemented using `AtomicU64` with `f64::to_bits()` and
52/// `f64::from_bits()` conversions. This preserves bit patterns exactly,
53/// including special values like NaN and infinity.
54///
55/// # Features
56///
57/// - Automatic memory ordering selection
58/// - Arithmetic operations via CAS loops
59/// - Zero-cost abstraction with inline methods
60/// - Access to underlying type via `inner()` for advanced use cases
61///
62/// # Limitations
63///
64/// - Arithmetic operations use CAS loops (slower than integer operations)
65/// - NaN values may cause unexpected behavior in CAS operations
66/// - No max/min operations (complex floating point semantics)
67///
68/// # Example
69///
70/// ```rust
71/// use prism3_rust_concurrent::atomic::AtomicF64;
72///
73/// let atomic = AtomicF64::new(3.14159);
74/// atomic.add(1.0);
75/// assert_eq!(atomic.load(), 4.14159);
76/// ```
77///
78/// # Author
79///
80/// Haixing Hu
81#[repr(transparent)]
82pub struct AtomicF64 {
83    inner: AtomicU64,
84}
85
86impl AtomicF64 {
87    /// Creates a new atomic floating point number.
88    ///
89    /// # Parameters
90    ///
91    /// * `value` - The initial value.
92    ///
93    /// # Example
94    ///
95    /// ```rust
96    /// use prism3_rust_concurrent::atomic::AtomicF64;
97    ///
98    /// let atomic = AtomicF64::new(3.14159);
99    /// assert_eq!(atomic.load(), 3.14159);
100    /// ```
101    #[inline]
102    pub fn new(value: f64) -> Self {
103        Self {
104            inner: AtomicU64::new(value.to_bits()),
105        }
106    }
107
108    /// Gets the current value.
109    ///
110    /// # Memory Ordering
111    ///
112    /// Uses `Acquire` ordering on the underlying `AtomicU64`. This ensures
113    /// that all writes from other threads that happened before a `Release`
114    /// store are visible after this load.
115    ///
116    /// # Returns
117    ///
118    /// The current value.
119    #[inline]
120    pub fn load(&self) -> f64 {
121        f64::from_bits(self.inner.load(Ordering::Acquire))
122    }
123
124    /// Sets a new value.
125    ///
126    /// # Memory Ordering
127    ///
128    /// Uses `Release` ordering on the underlying `AtomicU64`. This ensures
129    /// that all prior writes in this thread are visible to other threads
130    /// that perform an `Acquire` load.
131    ///
132    /// # Parameters
133    ///
134    /// * `value` - The new value to set.
135    #[inline]
136    pub fn store(&self, value: f64) {
137        self.inner.store(value.to_bits(), Ordering::Release);
138    }
139
140    /// Swaps the current value with a new value, returning the old value.
141    ///
142    /// # Memory Ordering
143    ///
144    /// Uses `AcqRel` ordering on the underlying `AtomicU64`. This provides
145    /// full synchronization for this read-modify-write operation.
146    ///
147    /// # Parameters
148    ///
149    /// * `value` - The new value to swap in.
150    ///
151    /// # Returns
152    ///
153    /// The old value.
154    #[inline]
155    pub fn swap(&self, value: f64) -> f64 {
156        f64::from_bits(self.inner.swap(value.to_bits(), Ordering::AcqRel))
157    }
158
159    /// Compares and sets the value atomically.
160    ///
161    /// If the current value equals `current`, sets it to `new` and returns
162    /// `Ok(())`. Otherwise, returns `Err(actual)` where `actual` is the
163    /// current value.
164    ///
165    /// # Memory Ordering
166    ///
167    /// - **Success**: Uses `AcqRel` ordering on the underlying `AtomicU64`
168    ///   to ensure full synchronization when the exchange succeeds.
169    /// - **Failure**: Uses `Acquire` ordering to observe the actual value
170    ///   written by another thread.
171    ///
172    /// # Parameters
173    ///
174    /// * `current` - The expected current value.
175    /// * `new` - The new value to set if current matches.
176    ///
177    /// # Returns
178    ///
179    /// `Ok(())` on success, or `Err(actual)` on failure.
180    #[inline]
181    pub fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
182        self.inner
183            .compare_exchange(
184                current.to_bits(),
185                new.to_bits(),
186                Ordering::AcqRel,
187                Ordering::Acquire,
188            )
189            .map(|_| ())
190            .map_err(f64::from_bits)
191    }
192
193    /// Weak version of compare-and-set.
194    ///
195    /// May spuriously fail even when the comparison succeeds. Should be used
196    /// in a loop.
197    ///
198    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
199    ///
200    /// # Parameters
201    ///
202    /// * `current` - The expected current value.
203    /// * `new` - The new value to set if current matches.
204    ///
205    /// # Returns
206    ///
207    /// `Ok(())` on success, or `Err(actual)` on failure.
208    #[inline]
209    pub fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
210        self.inner
211            .compare_exchange_weak(
212                current.to_bits(),
213                new.to_bits(),
214                Ordering::AcqRel,
215                Ordering::Acquire,
216            )
217            .map(|_| ())
218            .map_err(f64::from_bits)
219    }
220
221    /// Compares and exchanges the value atomically, returning the previous
222    /// value.
223    ///
224    /// If the current value equals `current`, sets it to `new` and returns
225    /// the old value. Otherwise, returns the actual current value.
226    ///
227    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
228    ///
229    /// # Parameters
230    ///
231    /// * `current` - The expected current value.
232    /// * `new` - The new value to set if current matches.
233    ///
234    /// # Returns
235    ///
236    /// The value before the operation.
237    #[inline]
238    pub fn compare_and_exchange(&self, current: f64, new: f64) -> f64 {
239        match self.inner.compare_exchange(
240            current.to_bits(),
241            new.to_bits(),
242            Ordering::AcqRel,
243            Ordering::Acquire,
244        ) {
245            Ok(prev_bits) => f64::from_bits(prev_bits),
246            Err(actual_bits) => f64::from_bits(actual_bits),
247        }
248    }
249
250    /// Weak version of compare-and-exchange.
251    ///
252    /// May spuriously fail even when the comparison succeeds. Should be used
253    /// in a loop.
254    ///
255    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
256    ///
257    /// # Parameters
258    ///
259    /// * `current` - The expected current value.
260    /// * `new` - The new value to set if current matches.
261    ///
262    /// # Returns
263    ///
264    /// The value before the operation.
265    #[inline]
266    pub fn compare_and_exchange_weak(&self, current: f64, new: f64) -> f64 {
267        match self.inner.compare_exchange_weak(
268            current.to_bits(),
269            new.to_bits(),
270            Ordering::AcqRel,
271            Ordering::Acquire,
272        ) {
273            Ok(prev_bits) => f64::from_bits(prev_bits),
274            Err(actual_bits) => f64::from_bits(actual_bits),
275        }
276    }
277
278    /// Atomically adds a value, returning the old value.
279    ///
280    /// # Memory Ordering
281    ///
282    /// Internally uses a CAS loop with `compare_set_weak`, which uses
283    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
284    /// eventual consistency even under high contention.
285    ///
286    /// # Performance
287    ///
288    /// May be slow in high-contention scenarios due to the CAS loop.
289    /// Consider using atomic integers if performance is critical.
290    ///
291    /// # Parameters
292    ///
293    /// * `delta` - The value to add.
294    ///
295    /// # Returns
296    ///
297    /// The old value before adding.
298    ///
299    /// # Example
300    ///
301    /// ```rust
302    /// use prism3_rust_concurrent::atomic::AtomicF64;
303    ///
304    /// let atomic = AtomicF64::new(10.0);
305    /// let old = atomic.fetch_add(5.5);
306    /// assert_eq!(old, 10.0);
307    /// assert_eq!(atomic.load(), 15.5);
308    /// ```
309    #[inline]
310    pub fn fetch_add(&self, delta: f64) -> f64 {
311        let mut current = self.load();
312        loop {
313            let new = current + delta;
314            match self.compare_set_weak(current, new) {
315                Ok(_) => return current,
316                Err(actual) => current = actual,
317            }
318        }
319    }
320
321    /// Atomically subtracts a value, returning the old value.
322    ///
323    /// # Memory Ordering
324    ///
325    /// Internally uses a CAS loop with `compare_set_weak`, which uses
326    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
327    /// eventual consistency even under high contention.
328    ///
329    /// # Parameters
330    ///
331    /// * `delta` - The value to subtract.
332    ///
333    /// # Returns
334    ///
335    /// The old value before subtracting.
336    ///
337    /// # Example
338    ///
339    /// ```rust
340    /// use prism3_rust_concurrent::atomic::AtomicF64;
341    ///
342    /// let atomic = AtomicF64::new(10.0);
343    /// let old = atomic.fetch_sub(3.5);
344    /// assert_eq!(old, 10.0);
345    /// assert_eq!(atomic.load(), 6.5);
346    /// ```
347    #[inline]
348    pub fn fetch_sub(&self, delta: f64) -> f64 {
349        let mut current = self.load();
350        loop {
351            let new = current - delta;
352            match self.compare_set_weak(current, new) {
353                Ok(_) => return current,
354                Err(actual) => current = actual,
355            }
356        }
357    }
358
359    /// Atomically multiplies by a factor, returning the old value.
360    ///
361    /// # Memory Ordering
362    ///
363    /// Internally uses a CAS loop with `compare_set_weak`, which uses
364    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
365    /// eventual consistency even under high contention.
366    ///
367    /// # Parameters
368    ///
369    /// * `factor` - The factor to multiply by.
370    ///
371    /// # Returns
372    ///
373    /// The old value before multiplying.
374    ///
375    /// # Example
376    ///
377    /// ```rust
378    /// use prism3_rust_concurrent::atomic::AtomicF64;
379    ///
380    /// let atomic = AtomicF64::new(10.0);
381    /// let old = atomic.fetch_mul(2.5);
382    /// assert_eq!(old, 10.0);
383    /// assert_eq!(atomic.load(), 25.0);
384    /// ```
385    #[inline]
386    pub fn fetch_mul(&self, factor: f64) -> f64 {
387        let mut current = self.load();
388        loop {
389            let new = current * factor;
390            match self.compare_set_weak(current, new) {
391                Ok(_) => return current,
392                Err(actual) => current = actual,
393            }
394        }
395    }
396
397    /// Atomically divides by a divisor, returning the old value.
398    ///
399    /// # Memory Ordering
400    ///
401    /// Internally uses a CAS loop with `compare_set_weak`, which uses
402    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
403    /// eventual consistency even under high contention.
404    ///
405    /// # Parameters
406    ///
407    /// * `divisor` - The divisor to divide by.
408    ///
409    /// # Returns
410    ///
411    /// The old value before dividing.
412    ///
413    /// # Example
414    ///
415    /// ```rust
416    /// use prism3_rust_concurrent::atomic::AtomicF64;
417    ///
418    /// let atomic = AtomicF64::new(10.0);
419    /// let old = atomic.fetch_div(2.0);
420    /// assert_eq!(old, 10.0);
421    /// assert_eq!(atomic.load(), 5.0);
422    /// ```
423    #[inline]
424    pub fn fetch_div(&self, divisor: f64) -> f64 {
425        let mut current = self.load();
426        loop {
427            let new = current / divisor;
428            match self.compare_set_weak(current, new) {
429                Ok(_) => return current,
430                Err(actual) => current = actual,
431            }
432        }
433    }
434
435    /// Updates the value using a function, returning the old value.
436    ///
437    /// # Memory Ordering
438    ///
439    /// Internally uses a CAS loop with `compare_set_weak`, which uses
440    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
441    /// eventual consistency even under high contention.
442    ///
443    /// # Parameters
444    ///
445    /// * `f` - A function that takes the current value and returns the new
446    ///   value.
447    ///
448    /// # Returns
449    ///
450    /// The old value before the update.
451    #[inline]
452    pub fn fetch_update<F>(&self, f: F) -> f64
453    where
454        F: Fn(f64) -> f64,
455    {
456        let mut current = self.load();
457        loop {
458            let new = f(current);
459            match self.compare_set_weak(current, new) {
460                Ok(_) => return current,
461                Err(actual) => current = actual,
462            }
463        }
464    }
465
466    /// Gets a reference to the underlying standard library atomic type.
467    ///
468    /// This allows direct access to the standard library's atomic operations
469    /// for advanced use cases that require fine-grained control over memory
470    /// ordering.
471    ///
472    /// # Memory Ordering
473    ///
474    /// When using the returned reference, you have full control over memory
475    /// ordering. Remember to use `f64::to_bits()` and `f64::from_bits()` for
476    /// conversions.
477    ///
478    /// # Returns
479    ///
480    /// A reference to the underlying `std::sync::atomic::AtomicU64`.
481    #[inline]
482    pub fn inner(&self) -> &AtomicU64 {
483        &self.inner
484    }
485}
486
487impl Atomic for AtomicF64 {
488    type Value = f64;
489
490    #[inline]
491    fn load(&self) -> f64 {
492        self.load()
493    }
494
495    #[inline]
496    fn store(&self, value: f64) {
497        self.store(value);
498    }
499
500    #[inline]
501    fn swap(&self, value: f64) -> f64 {
502        self.swap(value)
503    }
504
505    #[inline]
506    fn compare_set(&self, current: f64, new: f64) -> Result<(), f64> {
507        self.compare_set(current, new)
508    }
509
510    #[inline]
511    fn compare_set_weak(&self, current: f64, new: f64) -> Result<(), f64> {
512        self.compare_set_weak(current, new)
513    }
514
515    #[inline]
516    fn compare_exchange(&self, current: f64, new: f64) -> f64 {
517        self.compare_and_exchange(current, new)
518    }
519
520    #[inline]
521    fn compare_exchange_weak(&self, current: f64, new: f64) -> f64 {
522        self.compare_and_exchange_weak(current, new)
523    }
524
525    #[inline]
526    fn fetch_update<F>(&self, f: F) -> f64
527    where
528        F: Fn(f64) -> f64,
529    {
530        self.fetch_update(f)
531    }
532}
533
534impl AtomicNumber for AtomicF64 {
535    #[inline]
536    fn fetch_add(&self, delta: f64) -> f64 {
537        self.fetch_add(delta)
538    }
539
540    #[inline]
541    fn fetch_sub(&self, delta: f64) -> f64 {
542        self.fetch_sub(delta)
543    }
544
545    #[inline]
546    fn fetch_mul(&self, factor: f64) -> f64 {
547        self.fetch_mul(factor)
548    }
549
550    #[inline]
551    fn fetch_div(&self, divisor: f64) -> f64 {
552        self.fetch_div(divisor)
553    }
554}
555
556unsafe impl Send for AtomicF64 {}
557unsafe impl Sync for AtomicF64 {}
558
559impl Default for AtomicF64 {
560    #[inline]
561    fn default() -> Self {
562        Self::new(0.0)
563    }
564}
565
566impl From<f64> for AtomicF64 {
567    #[inline]
568    fn from(value: f64) -> Self {
569        Self::new(value)
570    }
571}
572
573impl fmt::Debug for AtomicF64 {
574    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575        f.debug_struct("AtomicF64")
576            .field("value", &self.load())
577            .finish()
578    }
579}
580
581impl fmt::Display for AtomicF64 {
582    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583        write!(f, "{}", self.load())
584    }
585}