1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
//! This crate provides [`AtomicF32`] and [`AtomicF64`] types. They're
//! implemented on top of `AtomicU32` and `AtomicU64` respectively.
//!
//! ```
//! # use atomic_float::AtomicF32;
//! # use std::sync::atomic::Ordering;
//! static DELTA_TIME: AtomicF32 = AtomicF32::new(1.0);
//!
//! // In some main simulation loop:
//! # fn compute_delta_time() -> f32 { 1.0 / 60.0 }
//! DELTA_TIME.store(compute_delta_time(), Ordering::Release);
//!
//! // elsewhere, perhaps on other threads:
//! let dt = DELTA_TIME.load(Ordering::Acquire);
//! // Use `dt` to compute simulation...
//! ```
//!
//! # Portability
//!
//! In general, this library is as portable as [`AtomicU32`]/[`AtomicU64`]
//! (fairly portable). See the module documentation for [core::sync::atomic] for
//! information about the portability of atomic operations as a whole.
//!
//! [`AtomicU32`]: core::sync::atomic::AtomicU32
//! [`AtomicU64`]: core::sync::atomic::AtomicU64
//!
//! Not every architecture has 64-bit atomics. As a result [`AtomicF64`] is
//! behind an on-by-default feature flag, called `atomic_f64`, and is explicitly
//! disabled on platforms known not to have 64-bit atomics (32-bit MIPs and
//! PowerPC targets).
//!
//! Because it's on-by-default, it's possible it will be enabled by accident. If
//! you're the person compiling the end-result (invoking `cargo build`), and
//! some crate has done this (e.g. you can't simply add
//! `default-features=false`) to a `Cargo.toml` line, you can override feature
//! selection and force-disable `AtomicF64` using the `force_disable_atomic64`
//! cfg (that is, by adding `RUSTFLAGS="--cfg=force_disable_atomic64"`).
//!
//! Let me know if you have to do this though, and I'll make consideration for
//! your target the way I have for MIPs and PowerPC.
//!
//! # Potential Use Cases
//!
//! The motivating cases for this were:
//!
//! - Tunable parameters loaded from a file that otherwise behaved as global
//!   constants (still compelling to me).
//!
//! - Global variables like time deltas (see example above) which would need to
//!   be threaded through a large amount of code. (Not as compelling).
//!
//! But really it was another 90% finished project that I had meant to get out
//! the door.
//!
//! # Performance
//!
//! On x86 and x86_64: basically 0 cost if you pick the right orderings and
//! stick to load/store.
//!
//! On everything else: acceptable if you pick the right orderings.
//!
//! In general, this depends on your architecture. If you're on x86{,_64}, you
//! can get away with a lot of dodgy atomic code. Even `SeqCst` usage won't bite
//! you too bad, so long as stores are rare. That said, I'd try to stick to
//! `Acquire`/`Release` even on x86. For load/store, this has roughly 0 cost
//! compared to write/read to a global variable directly. Also, if you just need
//! atomicity, and not any global orderings, feel free to use Relaxed.
//!
//! (I normally wouldn't give this advice, but you're probably not using
//! floating point in a situation where the exact value you get must follow
//! absolute rules).
//!
//! Beyond all of this, we provide a few convenient RMW operations. Ones that
//! have to perform actual float operations, such as `fetch_add`/`fetch_sub`
//! (but not ones that operate solely on the binary representation, like
//! `fetch_abs` or `fetch_neg`) need to perform a CAS loop. That means they're
//! much slower than `fetch_add`/`fetch_sub` are for AtomicU32, for example.
#![no_std]
#![deny(missing_docs)]

mod atomic_f32;
pub use atomic_f32::AtomicF32;

#[cfg(all(
    feature = "atomic_f64",
    not(any(target_arch = "powerpc", target_arch = "mips", force_disable_atomic64))
))]
mod atomic_f64;

#[cfg(all(
    feature = "atomic_f64",
    not(any(target_arch = "powerpc", target_arch = "mips", force_disable_atomic64))
))]
pub use atomic_f64::AtomicF64;

use core::sync::atomic::Ordering;

#[inline]
fn fail_order_for(order: Ordering) -> Ordering {
    match order {
        Ordering::Release | Ordering::Relaxed => Ordering::Relaxed,
        Ordering::Acquire | Ordering::AcqRel => Ordering::Acquire,
        Ordering::SeqCst => Ordering::SeqCst,
        o => o,
    }
}