Struct nih_plug::prelude::AtomicF32

#[repr(transparent)]
pub struct AtomicF32(_);
Expand description

A floating point type which can be safely shared between threads.

This type has the same in-memory representation as the underlying floating point type, f32.

See the module documentation for core::sync::atomic for information about the portability of various atomics (this one is mostly as portable as AtomicU32, with the caveat that we additionally that the platform support 32-bit floats).

Example

The intended use case is situations like this:

static DELTA_TIME: AtomicF32 = AtomicF32::new(1.0);

// In some main simulation loop:
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...

As well as any other cases where use of locking would be too substantial of overhead.

Note that when used like this (with Acquire and Release orderings), on x86_64 this compiles to the same code as you would get from a C++ global, (or a Rust static mut), while offering full synchronization.

(While caveats exist, the cases where you’d need the total order guaranteed by SeqCst for something like AtomicF32 seem very rare).

Implementation

Note: These details are not part of the stability guarantee of this crate, and are subject to change without a semver-breaking change.

Under the hood we use a transparent UnsafeCell<f32>, and cast the &UnsafeCell<f32> to an &AtomicU32 in order to perform atomic operations.

This means that we have the same ABI and layout as f32, and that some operations have a minimal cost (for example: on x86 all operations of equivalent or weaker ordering than Release stores/Acquire loads are essentially equivalent to non-atomic f32).

However, operations like fetch_add are considerably slower than would be the case for integer atomics.

Implementations§

§

impl AtomicF32

pub const fn new(float: f32) -> AtomicF32

Initialize a AtomicF32 from an f32.

Example

Use as a variable

let x = AtomicF32::new(3.0f32);
assert_eq!(x.load(Relaxed), 3.0f32);

Use as a static:

static A_STATIC: AtomicF32 = AtomicF32::new(800.0);
assert_eq!(A_STATIC.load(Relaxed), 800.0);

pub fn get_mut(&mut self) -> &mut f32

Returns a mutable reference to the underlying float.

This is safe because the mutable reference guarantees that no other threads are concurrently accessing the atomic data.

Example
let mut some_float = AtomicF32::new(1.0);
assert_eq!(*some_float.get_mut(), 1.0);
*some_float.get_mut() += 1.0;
assert_eq!(some_float.load(Ordering::SeqCst), 2.0);

pub fn into_inner(self) -> f32

Consumes the atomic and returns the contained value.

This is safe because passing self by value guarantees that no other threads are concurrently accessing the atomic data.

Example
let v = AtomicF32::new(6.0);
assert_eq!(v.into_inner(), 6.0f32);

pub fn load(&self, ordering: Ordering) -> f32

Loads a value from the atomic float.

load takes an Ordering argument which describes the memory ordering of this operation. Possible values are SeqCst, Acquire and Relaxed.

Panics

Panics if ordering is Release or AcqRel.

Example
use atomic_float::AtomicF32;
use std::sync::atomic::Ordering;
let v = AtomicF32::new(22.5);
assert_eq!(v.load(Ordering::SeqCst), 22.5);

pub fn store(&self, value: f32, ordering: Ordering)

Store a value into the atomic float.

store takes an Ordering argument which describes the memory ordering of this operation. Possible values are SeqCst, Release and Relaxed.

Panics

Panics if ordering is Acquire or AcqRel.

Example
use atomic_float::AtomicF32;
use std::sync::atomic::Ordering;
let v = AtomicF32::new(22.5);
assert_eq!(v.load(Ordering::SeqCst), 22.5);
v.store(30.0, Ordering::SeqCst);
assert_eq!(v.load(Ordering::SeqCst), 30.0);

pub fn swap(&self, new_value: f32, ordering: Ordering) -> f32

Stores a value into the atomic float, returning the previous value.

swap takes an Ordering argument which describes the memory ordering of this operation. All ordering modes are possible. Note that using Acquire makes the store part of this operation Relaxed, and using Release makes the load part Relaxed.

Example
use atomic_float::AtomicF32;
use std::sync::atomic::Ordering;
let v = AtomicF32::new(4.5);
assert_eq!(v.swap(100.0, Ordering::Relaxed), 4.5);
assert_eq!(v.load(Ordering::Relaxed), 100.0);

pub fn compare_and_swap(&self, current: f32, new: f32, order: Ordering) -> f32

Stores a value into the atomic float if the current value is bitwise identical to the current value.

The return value is always the previous value. If it is equal to current, then the value was updated.

compare_and_swap also takes an Ordering argument which describes the memory ordering of this operation. Notice that even when using AcqRel, the operation might fail and hence just perform an Acquire load, but not have Release semantics. Using Acquire makes the store part of this operation Relaxed if it happens, and using Release makes the load part Relaxed.

Caveats

As the current must be bitwise identical to the previous value, you should not get the current value using any sort of arithmetic (both because of rounding, and to avoid any situation where -0.0 and +0.0 would be compared). Additionally, on some platforms (WASM and ASM.js currently) LLVM will canonicalize NaNs during loads, which can cause unexpected behavior here — typically in the other direction (two values being unexpectedly equal).

In practice, typical patterns for CaS tend to avoid these issues, but you’re encouraged to avoid relying on the behavior of cas-family APIs in the face of rounding, signed zero, and NaNs.

Example
let v = AtomicF32::new(5.0);
assert_eq!(v.compare_and_swap(5.0, 10.0, Ordering::Relaxed), 5.0);
assert_eq!(v.load(Ordering::Relaxed), 10.0);

assert_eq!(v.compare_and_swap(6.0, 12.0, Ordering::Relaxed), 10.0);
assert_eq!(v.load(Ordering::Relaxed), 10.0);

pub fn compare_exchange( &self, current: f32, new: f32, success: Ordering, failure: Ordering ) -> Result<f32, f32>

Stores a value into the atomic float if the current value is the bitwise identical as the current value.

The return value is a result indicating whether the new value was written and containing the previous value. On success this value is guaranteed to be bitwise identical to current.

compare_exchange takes two Ordering arguments to describe the memory ordering of this operation. The first describes the required ordering if the operation succeeds while the second describes the required ordering when the operation fails. Using Acquire as success ordering makes the store part of this operation Relaxed, and using Release makes the successful load Relaxed. The failure ordering can only be SeqCst, Acquire or Relaxed and must be equivalent to or weaker than the success ordering.

Notes

Note that in many cases, when while loops where the condition contains a compare_exchange operation are better written to use a compare_exchange_weak in the condition instead (as on weakly ordered platforms like ARM, the compare_exchange operation itself can require a loop to perform).

Caveats

As the current parameter must be bitwise identical to the previous value, you should not get the current value using any sort of arithmetic (both because of rounding, and to avoid any situation where -0.0 and +0.0 would be compared). Additionally, on Wasm, in some cases NaN values have been known to cause problems for non-typical usage of this API. See AtomicF32::as_atomic_bits if performing the compare_exchange on the raw bits of this atomic float would solve an issue for you.

Example
let v = AtomicF32::new(5.0);
assert_eq!(v.compare_exchange(5.0, 10.0, Acquire, Relaxed), Ok(5.0));
assert_eq!(v.load(Relaxed), 10.0);

assert_eq!(v.compare_exchange(6.0, 12.0, SeqCst, Relaxed), Err(10.0));
assert_eq!(v.load(Relaxed), 10.0);

pub fn compare_exchange_weak( &self, current: f32, new: f32, success: Ordering, failure: Ordering ) -> Result<f32, f32>

Stores a value into the atomic integer if the current value is the same as the current value.

Unlike compare_exchange, this function is allowed to spuriously fail even when the comparison succeeds, which can result in more efficient code on some platforms. The return value is a result indicating whether the new value was written and containing the previous value.

compare_exchange_weak takes two Ordering arguments to describe the memory ordering of this operation. The first describes the required ordering if the operation succeeds while the second describes the required ordering when the operation fails. Using Acquire as success ordering makes the store part of this operation Relaxed, and using Release makes the successful load Relaxed. The failure ordering can only be SeqCst, Acquire or Relaxed and must be equivalent to or weaker than the success ordering.

Caveats

As the current parameter must be bitwise identical to the previous value, you should not get the current value using any sort of arithmetic (both because of rounding, and to avoid any situation where -0.0 and +0.0 would be compared). Additionally, on Wasm, in some cases NaN values have been known to cause problems for non-typical usage of this API. See AtomicF32::as_atomic_bits if performing the compare_exchange on the raw bits of this atomic float would solve an issue for you.

Example

Note that this sort of CaS loop should generally use fetch_update instead.

let v = AtomicF32::new(5.0);
let mut old = v.load(Relaxed);
loop {
    let new = old * 2.0;
    match v.compare_exchange_weak(old, new, SeqCst, Relaxed) {
        Ok(_) => break,
        Err(x) => old = x,
    }
}

pub fn fetch_update<F>( &self, set_order: Ordering, fetch_order: Ordering, update: F ) -> Result<f32, f32>where F: FnMut(f32) -> Option<f32>,

Fetches the value, and applies a function to it that returns an optional new value. Returns a Result of Ok(previous_value) if the function returned Some(_), else Err(previous_value).

Note: This may call the function multiple times if the value has been changed from other threads in the meantime, as long as the function returns Some(_), but the function will have been applied only once to the stored value.

fetch_update takes two Ordering arguments to describe the memory ordering of this operation. The first describes the required ordering for when the operation finally succeeds while the second describes the required ordering for loads. These correspond to the success and failure orderings of compare_exchange respectively.

Using Acquire as success ordering makes the store part of this operation Relaxed, and using Release makes the final successful load Relaxed. The (failed) load ordering can only be SeqCst, Acquire or Relaxed and must be equivalent to or weaker than the success ordering.

Example
let x = AtomicF32::new(7.0);

assert_eq!(x.fetch_update(SeqCst, SeqCst, |_| None), Err(7.0));
assert_eq!(x.fetch_update(SeqCst, SeqCst, |x| Some(x + 1.0)), Ok(7.0));
assert_eq!(x.fetch_update(SeqCst, SeqCst, |x| Some(x + 1.0)), Ok(8.0));
assert_eq!(x.load(SeqCst), 9.0);

pub fn fetch_add(&self, val: f32, order: Ordering) -> f32

Adds to the current value, returning the previous value.

Because this returns the previous value, you may want to call it like: atom.fetch_add(x, order) + x

Examples
let x = AtomicF32::new(7.0);

assert_eq!(x.fetch_add(2.0, Relaxed), 7.0);
assert_eq!(x.fetch_add(1.0, SeqCst), 9.0);
assert_eq!(x.fetch_add(-100.0, AcqRel), 10.0);

pub fn fetch_sub(&self, val: f32, order: Ordering) -> f32

Subtract from the current value, returning the previous value.

Because this returns the previous value, you may want to call it like: atom.fetch_sub(x, order) - x

Note: This operation uses fetch_update under the hood, and is likely to be slower than the equivalent operation for atomic integers.

Examples
let x = AtomicF32::new(7.0);
assert_eq!(x.fetch_sub(2.0, Relaxed), 7.0);
assert_eq!(x.fetch_sub(-1.0, SeqCst), 5.0);
assert_eq!(x.fetch_sub(0.5, AcqRel), 6.0);

pub fn fetch_abs(&self, order: Ordering) -> f32

Produce the absolute value of the current value, returning the previous value.

Examples
let x = AtomicF32::new(-7.0);
assert_eq!(x.fetch_abs(Relaxed), -7.0);
assert_eq!(x.fetch_abs(SeqCst), 7.0);

pub fn fetch_neg(&self, order: Ordering) -> f32

Negates the current value, returning the previous value.

As a result of returning the previous value, you may want to invoke it like: -atom.fetch_neg(Relaxed).

Examples
let x = AtomicF32::new(-7.0);
assert_eq!(x.fetch_neg(Relaxed), -7.0);
assert_eq!(x.fetch_neg(SeqCst), 7.0);
assert_eq!(x.fetch_neg(AcqRel), -7.0);

pub fn fetch_min(&self, value: f32, order: Ordering) -> f32

Minimum with the current value.

Finds the minimum of the current value and the argument val, and sets the new value to the result.

Returns the previous value. Because of this, you may want to call it like: atom.fetch_min(x, order).min(x)

Note: This operation uses fetch_update under the hood, and is likely to be slower than the equivalent operation for atomic integers.

Examples

let foo = AtomicF32::new(23.0);
assert_eq!(foo.fetch_min(42.0, Ordering::Relaxed), 23.0);
assert_eq!(foo.load(Ordering::Relaxed), 23.0);
assert_eq!(foo.fetch_min(22.0, Ordering::Relaxed), 23.0);
assert_eq!(foo.load(Ordering::Relaxed), 22.0);

pub fn fetch_max(&self, value: f32, order: Ordering) -> f32

Maximum with the current value.

Finds the maximum of the current value and the argument val, and sets the new value to the result.

Returns the previous value. Because of this, you may want to call it like: atom.fetch_max(x, order).max(x)

Note: This operation uses fetch_update under the hood, and is likely to be slower than the equivalent operation for atomic integers.

Examples

let foo = AtomicF32::new(23.0);
assert_eq!(foo.fetch_max(22.0, Ordering::Relaxed), 23.0);
assert_eq!(foo.load(Ordering::Relaxed), 23.0);
assert_eq!(foo.fetch_max(42.0, Ordering::Relaxed), 23.0);
assert_eq!(foo.load(Ordering::Relaxed), 42.0);

pub fn as_atomic_bits(&self) -> &AtomicU32

Returns a reference to an atomic integer which can be used to access the atomic float’s underlying bits in a thread safe manner.

This is essentially a transmute::<&Self, &AtomicU32>(self), and is zero cost.

Motivation

This is exposed as an escape hatch because of the caveats around the AtomicF32 CaS-family APIs (compare_and_swap, compare_exchange, compare_exchange_weak, …) and the notion of bitwise identicality which they require being somewhat problematic for NaNs, especially on targets like Wasm (see rust-lang/rust#73328).

In general despite how bad this might sound, in practice we’re fairly safe: LLVM almost never optimizes through atomic operations, this library is written to try to avoid potential issues from most naive usage, and I’m optimistic the situation will clean itself up in the short-to-medium-term future.

However, if you need peace of mind, or find yourself in a case where you suspect you’re hitting this issue, you can access the underlying atomic value using this function.

Examples
let v = AtomicF32::new(22.5);
assert_eq!(v.as_atomic_bits().load(Ordering::Relaxed), 22.5f32.to_bits());

Trait Implementations§

§

impl Debug for AtomicF32

Equivalent to <f32 as core::fmt::Debug>::fmt.

Example

let v = AtomicF32::new(40.0);
assert_eq!(format!("{:?}", v), format!("{:?}", 40.0f32));
§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Default for AtomicF32

Return a zero-initialized atomic.

Example

let x = AtomicF32::default();
assert_eq!(x.load(Ordering::SeqCst), 0.0);
§

fn default() -> AtomicF32

Returns the “default value” for a type. Read more
§

impl From<f32> for AtomicF32

Equivalent to AtomicF32::new.

Example

let v = AtomicF32::from(10.0);
assert_eq!(v.load(Ordering::SeqCst), 10.0);
§

fn from(f: f32) -> AtomicF32

Converts to this type from the input type.
source§

impl PersistentField<'_, f32> for AtomicF32

source§

fn set(&self, new_value: f32)

Update the stored T value using interior mutability.
source§

fn map<F, R>(&self, f: F) -> Rwhere F: Fn(&f32) -> R,

Get a reference to the stored T value, and apply a function to it. This is used to serialize the T value.
§

impl Send for AtomicF32

§

impl Sync for AtomicF32

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<S> FromSample<S> for S

§

fn from_sample_(s: S) -> S

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Pointable for T

§

const ALIGN: usize = mem::align_of::<T>()

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T, U> ToSample<U> for Twhere U: FromSample<T>,

§

fn to_sample_(self) -> U

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> Any for Twhere T: Any,

§

impl<S, T> Duplex<S> for Twhere T: FromSample<S> + ToSample<S>,