prism3_atomic/atomic/
atomic_f32.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025.
4 *    3-Prism Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # Atomic 32-bit Floating Point
11//!
12//! Provides an easy-to-use atomic 32-bit floating point type with sensible
13//! default memory orderings. Implemented using bit conversion with AtomicU32.
14//!
15//! # Author
16//!
17//! Haixing Hu
18
19use std::fmt;
20use std::sync::atomic::AtomicU32;
21use std::sync::atomic::Ordering;
22
23use crate::atomic::traits::Atomic;
24use crate::atomic::traits::AtomicNumber;
25
26/// Atomic 32-bit floating point number.
27///
28/// Provides easy-to-use atomic operations with automatic memory ordering
29/// selection. Implemented using `AtomicU32` 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 `AtomicU32` with `f32::to_bits()` and
52/// `f32::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::AtomicF32;
72/// use std::sync::Arc;
73/// use std::thread;
74///
75/// let sum = Arc::new(AtomicF32::new(0.0));
76/// let mut handles = vec![];
77///
78/// for _ in 0..10 {
79///     let sum = sum.clone();
80///     let handle = thread::spawn(move || {
81///         for _ in 0..100 {
82///             sum.add(0.1);
83///         }
84///     });
85///     handles.push(handle);
86/// }
87///
88/// for handle in handles {
89///     handle.join().unwrap();
90/// }
91///
92/// // Note: Due to floating point precision, result may not be exactly 100.0
93/// let result = sum.load();
94/// assert!((result - 100.0).abs() < 0.01);
95/// ```
96///
97/// # Author
98///
99/// Haixing Hu
100#[repr(transparent)]
101pub struct AtomicF32 {
102    inner: AtomicU32,
103}
104
105impl AtomicF32 {
106    /// Creates a new atomic floating point number.
107    ///
108    /// # Parameters
109    ///
110    /// * `value` - The initial value.
111    ///
112    /// # Example
113    ///
114    /// ```rust
115    /// use prism3_rust_concurrent::atomic::AtomicF32;
116    ///
117    /// let atomic = AtomicF32::new(3.14);
118    /// assert_eq!(atomic.load(), 3.14);
119    /// ```
120    #[inline]
121    pub fn new(value: f32) -> Self {
122        Self {
123            inner: AtomicU32::new(value.to_bits()),
124        }
125    }
126
127    /// Gets the current value.
128    ///
129    /// # Memory Ordering
130    ///
131    /// Uses `Acquire` ordering on the underlying `AtomicU32`. This ensures
132    /// that all writes from other threads that happened before a `Release`
133    /// store are visible after this load.
134    ///
135    /// # Returns
136    ///
137    /// The current value.
138    ///
139    /// # Example
140    ///
141    /// ```rust
142    /// use prism3_rust_concurrent::atomic::AtomicF32;
143    ///
144    /// let atomic = AtomicF32::new(3.14);
145    /// assert_eq!(atomic.load(), 3.14);
146    /// ```
147    #[inline]
148    pub fn load(&self) -> f32 {
149        f32::from_bits(self.inner.load(Ordering::Acquire))
150    }
151
152    /// Sets a new value.
153    ///
154    /// # Memory Ordering
155    ///
156    /// Uses `Release` ordering on the underlying `AtomicU32`. This ensures
157    /// that all prior writes in this thread are visible to other threads
158    /// that perform an `Acquire` load.
159    ///
160    /// # Parameters
161    ///
162    /// * `value` - The new value to set.
163    ///
164    /// # Example
165    ///
166    /// ```rust
167    /// use prism3_rust_concurrent::atomic::AtomicF32;
168    ///
169    /// let atomic = AtomicF32::new(0.0);
170    /// atomic.store(3.14);
171    /// assert_eq!(atomic.load(), 3.14);
172    /// ```
173    #[inline]
174    pub fn store(&self, value: f32) {
175        self.inner.store(value.to_bits(), Ordering::Release);
176    }
177
178    /// Swaps the current value with a new value, returning the old value.
179    ///
180    /// # Memory Ordering
181    ///
182    /// Uses `AcqRel` ordering on the underlying `AtomicU32`. This provides
183    /// full synchronization for this read-modify-write operation.
184    ///
185    /// # Parameters
186    ///
187    /// * `value` - The new value to swap in.
188    ///
189    /// # Returns
190    ///
191    /// The old value.
192    ///
193    /// # Example
194    ///
195    /// ```rust
196    /// use prism3_rust_concurrent::atomic::AtomicF32;
197    ///
198    /// let atomic = AtomicF32::new(1.0);
199    /// let old = atomic.swap(2.0);
200    /// assert_eq!(old, 1.0);
201    /// assert_eq!(atomic.load(), 2.0);
202    /// ```
203    #[inline]
204    pub fn swap(&self, value: f32) -> f32 {
205        f32::from_bits(self.inner.swap(value.to_bits(), Ordering::AcqRel))
206    }
207
208    /// Compares and sets the value atomically.
209    ///
210    /// If the current value equals `current`, sets it to `new` and returns
211    /// `Ok(())`. Otherwise, returns `Err(actual)` where `actual` is the
212    /// current value.
213    ///
214    /// # Memory Ordering
215    ///
216    /// - **Success**: Uses `AcqRel` ordering on the underlying `AtomicU32`
217    ///   to ensure full synchronization when the exchange succeeds.
218    /// - **Failure**: Uses `Acquire` ordering to observe the actual value
219    ///   written by another thread.
220    ///
221    /// # Parameters
222    ///
223    /// * `current` - The expected current value.
224    /// * `new` - The new value to set if current matches.
225    ///
226    /// # Returns
227    ///
228    /// `Ok(())` on success, or `Err(actual)` on failure.
229    ///
230    /// # Warning
231    ///
232    /// Due to NaN != NaN, CAS operations with NaN values may behave
233    /// unexpectedly. Avoid using NaN in atomic floating point operations.
234    ///
235    /// # Example
236    ///
237    /// ```rust
238    /// use prism3_rust_concurrent::atomic::AtomicF32;
239    ///
240    /// let atomic = AtomicF32::new(1.0);
241    /// assert!(atomic.compare_set(1.0, 2.0).is_ok());
242    /// assert_eq!(atomic.load(), 2.0);
243    /// ```
244    #[inline]
245    pub fn compare_set(&self, current: f32, new: f32) -> Result<(), f32> {
246        self.inner
247            .compare_exchange(
248                current.to_bits(),
249                new.to_bits(),
250                Ordering::AcqRel,
251                Ordering::Acquire,
252            )
253            .map(|_| ())
254            .map_err(f32::from_bits)
255    }
256
257    /// Weak version of compare-and-set.
258    ///
259    /// May spuriously fail even when the comparison succeeds. Should be used
260    /// in a loop.
261    ///
262    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
263    ///
264    /// # Parameters
265    ///
266    /// * `current` - The expected current value.
267    /// * `new` - The new value to set if current matches.
268    ///
269    /// # Returns
270    ///
271    /// `Ok(())` on success, or `Err(actual)` on failure.
272    ///
273    /// # Example
274    ///
275    /// ```rust
276    /// use prism3_rust_concurrent::atomic::AtomicF32;
277    ///
278    /// let atomic = AtomicF32::new(1.0);
279    /// let mut current = atomic.load();
280    /// loop {
281    ///     match atomic.compare_set_weak(current, current + 1.0) {
282    ///         Ok(_) => break,
283    ///         Err(actual) => current = actual,
284    ///     }
285    /// }
286    /// assert_eq!(atomic.load(), 2.0);
287    /// ```
288    #[inline]
289    pub fn compare_set_weak(&self, current: f32, new: f32) -> Result<(), f32> {
290        self.inner
291            .compare_exchange_weak(
292                current.to_bits(),
293                new.to_bits(),
294                Ordering::AcqRel,
295                Ordering::Acquire,
296            )
297            .map(|_| ())
298            .map_err(f32::from_bits)
299    }
300
301    /// Compares and exchanges the value atomically, returning the previous
302    /// value.
303    ///
304    /// If the current value equals `current`, sets it to `new` and returns
305    /// the old value. Otherwise, returns the actual current value.
306    ///
307    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
308    ///
309    /// # Parameters
310    ///
311    /// * `current` - The expected current value.
312    /// * `new` - The new value to set if current matches.
313    ///
314    /// # Returns
315    ///
316    /// The value before the operation.
317    ///
318    /// # Example
319    ///
320    /// ```rust
321    /// use prism3_rust_concurrent::atomic::AtomicF32;
322    ///
323    /// let atomic = AtomicF32::new(1.0);
324    /// let prev = atomic.compare_and_exchange(1.0, 2.0);
325    /// assert_eq!(prev, 1.0);
326    /// assert_eq!(atomic.load(), 2.0);
327    /// ```
328    #[inline]
329    pub fn compare_and_exchange(&self, current: f32, new: f32) -> f32 {
330        match self.inner.compare_exchange(
331            current.to_bits(),
332            new.to_bits(),
333            Ordering::AcqRel,
334            Ordering::Acquire,
335        ) {
336            Ok(prev_bits) => f32::from_bits(prev_bits),
337            Err(actual_bits) => f32::from_bits(actual_bits),
338        }
339    }
340
341    /// Weak version of compare-and-exchange.
342    ///
343    /// May spuriously fail even when the comparison succeeds. Should be used
344    /// in a loop.
345    ///
346    /// Uses `AcqRel` ordering on success and `Acquire` ordering on failure.
347    ///
348    /// # Parameters
349    ///
350    /// * `current` - The expected current value.
351    /// * `new` - The new value to set if current matches.
352    ///
353    /// # Returns
354    ///
355    /// The value before the operation.
356    ///
357    /// # Example
358    ///
359    /// ```rust
360    /// use prism3_rust_concurrent::atomic::AtomicF32;
361    ///
362    /// let atomic = AtomicF32::new(1.0);
363    /// let mut current = atomic.load();
364    /// loop {
365    ///     let prev = atomic.compare_and_exchange_weak(current, current + 1.0);
366    ///     if prev == current {
367    ///         break;
368    ///     }
369    ///     current = prev;
370    /// }
371    /// assert_eq!(atomic.load(), 2.0);
372    /// ```
373    #[inline]
374    pub fn compare_and_exchange_weak(&self, current: f32, new: f32) -> f32 {
375        match self.inner.compare_exchange_weak(
376            current.to_bits(),
377            new.to_bits(),
378            Ordering::AcqRel,
379            Ordering::Acquire,
380        ) {
381            Ok(prev_bits) => f32::from_bits(prev_bits),
382            Err(actual_bits) => f32::from_bits(actual_bits),
383        }
384    }
385
386    /// Atomically adds a value, returning the old value.
387    ///
388    /// # Memory Ordering
389    ///
390    /// Internally uses a CAS loop with `compare_set_weak`, which uses
391    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
392    /// eventual consistency even under high contention.
393    ///
394    /// # Performance
395    ///
396    /// May be slow in high-contention scenarios due to the CAS loop.
397    /// Consider using atomic integers if performance is critical.
398    ///
399    /// # Parameters
400    ///
401    /// * `delta` - The value to add.
402    ///
403    /// # Returns
404    ///
405    /// The old value before adding.
406    ///
407    /// # Example
408    ///
409    /// ```rust
410    /// use prism3_rust_concurrent::atomic::AtomicF32;
411    ///
412    /// let atomic = AtomicF32::new(10.0);
413    /// let old = atomic.fetch_add(5.5);
414    /// assert_eq!(old, 10.0);
415    /// assert_eq!(atomic.load(), 15.5);
416    /// ```
417    #[inline]
418    pub fn fetch_add(&self, delta: f32) -> f32 {
419        let mut current = self.load();
420        loop {
421            let new = current + delta;
422            match self.compare_set_weak(current, new) {
423                Ok(_) => return current,
424                Err(actual) => current = actual,
425            }
426        }
427    }
428
429    /// Atomically subtracts a value, returning the old value.
430    ///
431    /// # Memory Ordering
432    ///
433    /// Internally uses a CAS loop with `compare_set_weak`, which uses
434    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
435    /// eventual consistency even under high contention.
436    ///
437    /// # Parameters
438    ///
439    /// * `delta` - The value to subtract.
440    ///
441    /// # Returns
442    ///
443    /// The old value before subtracting.
444    ///
445    /// # Example
446    ///
447    /// ```rust
448    /// use prism3_rust_concurrent::atomic::AtomicF32;
449    ///
450    /// let atomic = AtomicF32::new(10.0);
451    /// let old = atomic.fetch_sub(3.5);
452    /// assert_eq!(old, 10.0);
453    /// assert_eq!(atomic.load(), 6.5);
454    /// ```
455    #[inline]
456    pub fn fetch_sub(&self, delta: f32) -> f32 {
457        let mut current = self.load();
458        loop {
459            let new = current - delta;
460            match self.compare_set_weak(current, new) {
461                Ok(_) => return current,
462                Err(actual) => current = actual,
463            }
464        }
465    }
466
467    /// Atomically multiplies by a factor, returning the old value.
468    ///
469    /// # Memory Ordering
470    ///
471    /// Internally uses a CAS loop with `compare_set_weak`, which uses
472    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
473    /// eventual consistency even under high contention.
474    ///
475    /// # Parameters
476    ///
477    /// * `factor` - The factor to multiply by.
478    ///
479    /// # Returns
480    ///
481    /// The old value before multiplying.
482    ///
483    /// # Example
484    ///
485    /// ```rust
486    /// use prism3_rust_concurrent::atomic::AtomicF32;
487    ///
488    /// let atomic = AtomicF32::new(10.0);
489    /// let old = atomic.fetch_mul(2.5);
490    /// assert_eq!(old, 10.0);
491    /// assert_eq!(atomic.load(), 25.0);
492    /// ```
493    #[inline]
494    pub fn fetch_mul(&self, factor: f32) -> f32 {
495        let mut current = self.load();
496        loop {
497            let new = current * factor;
498            match self.compare_set_weak(current, new) {
499                Ok(_) => return current,
500                Err(actual) => current = actual,
501            }
502        }
503    }
504
505    /// Atomically divides by a divisor, returning the old value.
506    ///
507    /// # Memory Ordering
508    ///
509    /// Internally uses a CAS loop with `compare_set_weak`, which uses
510    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
511    /// eventual consistency even under high contention.
512    ///
513    /// # Parameters
514    ///
515    /// * `divisor` - The divisor to divide by.
516    ///
517    /// # Returns
518    ///
519    /// The old value before dividing.
520    ///
521    /// # Example
522    ///
523    /// ```rust
524    /// use prism3_rust_concurrent::atomic::AtomicF32;
525    ///
526    /// let atomic = AtomicF32::new(10.0);
527    /// let old = atomic.fetch_div(2.0);
528    /// assert_eq!(old, 10.0);
529    /// assert_eq!(atomic.load(), 5.0);
530    /// ```
531    #[inline]
532    pub fn fetch_div(&self, divisor: f32) -> f32 {
533        let mut current = self.load();
534        loop {
535            let new = current / divisor;
536            match self.compare_set_weak(current, new) {
537                Ok(_) => return current,
538                Err(actual) => current = actual,
539            }
540        }
541    }
542
543    /// Updates the value using a function, returning the old value.
544    ///
545    /// # Memory Ordering
546    ///
547    /// Internally uses a CAS loop with `compare_set_weak`, which uses
548    /// `AcqRel` on success and `Acquire` on failure. The loop ensures
549    /// eventual consistency even under high contention.
550    ///
551    /// # Parameters
552    ///
553    /// * `f` - A function that takes the current value and returns the new
554    ///   value.
555    ///
556    /// # Returns
557    ///
558    /// The old value before the update.
559    ///
560    /// # Example
561    ///
562    /// ```rust
563    /// use prism3_rust_concurrent::atomic::AtomicF32;
564    ///
565    /// let atomic = AtomicF32::new(10.0);
566    /// let old = atomic.fetch_update(|x| x * 2.0);
567    /// assert_eq!(old, 10.0);
568    /// assert_eq!(atomic.load(), 20.0);
569    /// ```
570    #[inline]
571    pub fn fetch_update<F>(&self, f: F) -> f32
572    where
573        F: Fn(f32) -> f32,
574    {
575        let mut current = self.load();
576        loop {
577            let new = f(current);
578            match self.compare_set_weak(current, new) {
579                Ok(_) => return current,
580                Err(actual) => current = actual,
581            }
582        }
583    }
584
585    /// Gets a reference to the underlying standard library atomic type.
586    ///
587    /// This allows direct access to the standard library's atomic operations
588    /// for advanced use cases that require fine-grained control over memory
589    /// ordering.
590    ///
591    /// # Memory Ordering
592    ///
593    /// When using the returned reference, you have full control over memory
594    /// ordering. Remember to use `f32::to_bits()` and `f32::from_bits()` for
595    /// conversions.
596    ///
597    /// # Returns
598    ///
599    /// A reference to the underlying `std::sync::atomic::AtomicU32`.
600    ///
601    /// # Example
602    ///
603    /// ```rust
604    /// use prism3_rust_concurrent::atomic::AtomicF32;
605    /// use std::sync::atomic::Ordering;
606    ///
607    /// let atomic = AtomicF32::new(0.0);
608    /// atomic.inner().store(3.14_f32.to_bits(), Ordering::Relaxed);
609    /// let bits = atomic.inner().load(Ordering::Relaxed);
610    /// assert_eq!(f32::from_bits(bits), 3.14);
611    /// ```
612    #[inline]
613    pub fn inner(&self) -> &AtomicU32 {
614        &self.inner
615    }
616}
617
618impl Atomic for AtomicF32 {
619    type Value = f32;
620
621    #[inline]
622    fn load(&self) -> f32 {
623        self.load()
624    }
625
626    #[inline]
627    fn store(&self, value: f32) {
628        self.store(value);
629    }
630
631    #[inline]
632    fn swap(&self, value: f32) -> f32 {
633        self.swap(value)
634    }
635
636    #[inline]
637    fn compare_set(&self, current: f32, new: f32) -> Result<(), f32> {
638        self.compare_set(current, new)
639    }
640
641    #[inline]
642    fn compare_set_weak(&self, current: f32, new: f32) -> Result<(), f32> {
643        self.compare_set_weak(current, new)
644    }
645
646    #[inline]
647    fn compare_exchange(&self, current: f32, new: f32) -> f32 {
648        self.compare_and_exchange(current, new)
649    }
650
651    #[inline]
652    fn compare_exchange_weak(&self, current: f32, new: f32) -> f32 {
653        self.compare_and_exchange_weak(current, new)
654    }
655
656    #[inline]
657    fn fetch_update<F>(&self, f: F) -> f32
658    where
659        F: Fn(f32) -> f32,
660    {
661        self.fetch_update(f)
662    }
663}
664
665impl AtomicNumber for AtomicF32 {
666    #[inline]
667    fn fetch_add(&self, delta: f32) -> f32 {
668        self.fetch_add(delta)
669    }
670
671    #[inline]
672    fn fetch_sub(&self, delta: f32) -> f32 {
673        self.fetch_sub(delta)
674    }
675
676    #[inline]
677    fn fetch_mul(&self, factor: f32) -> f32 {
678        self.fetch_mul(factor)
679    }
680
681    #[inline]
682    fn fetch_div(&self, divisor: f32) -> f32 {
683        self.fetch_div(divisor)
684    }
685}
686
687unsafe impl Send for AtomicF32 {}
688unsafe impl Sync for AtomicF32 {}
689
690impl Default for AtomicF32 {
691    #[inline]
692    fn default() -> Self {
693        Self::new(0.0)
694    }
695}
696
697impl From<f32> for AtomicF32 {
698    #[inline]
699    fn from(value: f32) -> Self {
700        Self::new(value)
701    }
702}
703
704impl fmt::Debug for AtomicF32 {
705    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706        f.debug_struct("AtomicF32")
707            .field("value", &self.load())
708            .finish()
709    }
710}
711
712impl fmt::Display for AtomicF32 {
713    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
714        write!(f, "{}", self.load())
715    }
716}