qubit-atomic 0.10.3

User-friendly atomic operations wrapper providing JDK-like atomic API
Documentation
/*******************************************************************************
 *
 *    Copyright (c) 2025 - 2026 Haixing Hu.
 *
 *    SPDX-License-Identifier: Apache-2.0
 *
 *    Licensed under the Apache License, Version 2.0.
 *
 ******************************************************************************/

use std::cell::Cell;
use std::sync::atomic::Ordering;

use qubit_atomic::Atomic;

#[test]
fn test_default_from_debug_display_impls() {
    let bool_default = Atomic::<bool>::default();
    assert!(!bool_default.load());
    let bool_from = Atomic::<bool>::from(true);
    assert_eq!(format!("{bool_from:?}"), "Atomic { value: true }");
    assert_eq!(format!("{bool_from}"), "true");

    let int_default = Atomic::<i32>::default();
    assert_eq!(int_default.load(), 0);
    let int_from = Atomic::<i32>::from(42);
    assert_eq!(format!("{int_from:?}"), "Atomic { value: 42 }");
    assert_eq!(format!("{int_from}"), "42");

    let float_default = Atomic::<f32>::default();
    assert_eq!(float_default.load(), 0.0);
    let float_from = Atomic::<f32>::from(1.5);
    assert_eq!(format!("{float_from:?}"), "Atomic { value: 1.5 }");
    assert_eq!(format!("{float_from}"), "1.5");

    let double_default = Atomic::<f64>::default();
    assert_eq!(double_default.load(), 0.0);
    let double_from = Atomic::<f64>::from(2.5);
    assert_eq!(format!("{double_from:?}"), "Atomic { value: 2.5 }");
    assert_eq!(format!("{double_from}"), "2.5");
}

#[test]
fn test_fetch_update_retry_paths() {
    let bool_atomic = Atomic::<bool>::new(false);
    let bool_raced = Cell::new(false);
    let bool_old = bool_atomic.fetch_update(|current| {
        if !bool_raced.replace(true) {
            bool_atomic.store(!current);
        }
        !current
    });
    assert!(bool_old);
    assert!(!bool_atomic.load());

    let int_atomic = Atomic::<i32>::new(1);
    let int_raced = Cell::new(false);
    let int_old = int_atomic.fetch_update(|current| {
        if !int_raced.replace(true) {
            int_atomic.store(current + 10);
        }
        current * 2
    });
    assert_eq!(int_old, 11);
    assert_eq!(int_atomic.load(), 22);

    let float_atomic = Atomic::<f32>::new(1.0);
    let float_raced = Cell::new(false);
    let float_old = float_atomic.fetch_update(|current| {
        if !float_raced.replace(true) {
            float_atomic.store(current + 10.0);
        }
        current * 2.0
    });
    assert_eq!(float_old, 11.0);
    assert_eq!(float_atomic.load(), 22.0);

    let double_atomic = Atomic::<f64>::new(1.0);
    let double_raced = Cell::new(false);
    let double_old = double_atomic.fetch_update(|current| {
        if !double_raced.replace(true) {
            double_atomic.store(current + 10.0);
        }
        current * 2.0
    });
    assert_eq!(double_old, 11.0);
    assert_eq!(double_atomic.load(), 22.0);
}

#[test]
fn test_try_update_success_and_reject_paths() {
    let bool_atomic = Atomic::<bool>::new(false);
    assert_eq!(
        bool_atomic.try_update(|current| (!current).then_some(true)),
        Some(false),
    );
    assert!(bool_atomic.load());
    assert_eq!(
        bool_atomic.try_update(|current| (!current).then_some(true)),
        None,
    );
    assert!(bool_atomic.load());

    let int_atomic = Atomic::<i32>::new(3);
    assert_eq!(
        int_atomic.try_update(|current| (current % 2 == 1).then_some(current + 1)),
        Some(3),
    );
    assert_eq!(int_atomic.load(), 4);
    assert_eq!(
        int_atomic.try_update(|current| (current % 2 == 1).then_some(current + 1)),
        None,
    );
    assert_eq!(int_atomic.load(), 4);

    let float_atomic = Atomic::<f32>::new(1.5);
    assert_eq!(
        float_atomic.try_update(|current| (current > 0.0).then_some(current * 2.0)),
        Some(1.5),
    );
    assert_eq!(float_atomic.load(), 3.0);
    assert_eq!(
        float_atomic.try_update(|current| (current < 0.0).then_some(current * 2.0)),
        None,
    );
    assert_eq!(float_atomic.load(), 3.0);

    let double_atomic = Atomic::<f64>::new(1.5);
    assert_eq!(
        double_atomic.try_update(|current| (current > 0.0).then_some(current * 2.0)),
        Some(1.5),
    );
    assert_eq!(double_atomic.load(), 3.0);
    assert_eq!(
        double_atomic.try_update(|current| (current < 0.0).then_some(current * 2.0)),
        None,
    );
    assert_eq!(double_atomic.load(), 3.0);
}

#[test]
fn test_try_update_retry_paths() {
    let bool_atomic = Atomic::<bool>::new(false);
    let bool_raced = Cell::new(false);
    let bool_old = bool_atomic.try_update(|current| {
        if !bool_raced.replace(true) {
            bool_atomic.store(!current);
        }
        Some(!current)
    });
    assert_eq!(bool_old, Some(true));
    assert!(!bool_atomic.load());

    let int_atomic = Atomic::<i32>::new(1);
    let int_raced = Cell::new(false);
    let int_old = int_atomic.try_update(|current| {
        if !int_raced.replace(true) {
            int_atomic.store(current + 10);
        }
        Some(current * 2)
    });
    assert_eq!(int_old, Some(11));
    assert_eq!(int_atomic.load(), 22);

    let float_atomic = Atomic::<f32>::new(1.0);
    let float_raced = Cell::new(false);
    let float_old = float_atomic.try_update(|current| {
        if !float_raced.replace(true) {
            float_atomic.store(current + 10.0);
        }
        Some(current * 2.0)
    });
    assert_eq!(float_old, Some(11.0));
    assert_eq!(float_atomic.load(), 22.0);

    let double_atomic = Atomic::<f64>::new(1.0);
    let double_raced = Cell::new(false);
    let double_old = double_atomic.try_update(|current| {
        if !double_raced.replace(true) {
            double_atomic.store(current + 10.0);
        }
        Some(current * 2.0)
    });
    assert_eq!(double_old, Some(11.0));
    assert_eq!(double_atomic.load(), 22.0);
}

#[test]
fn test_f32_compare_exchange_uses_raw_bits() {
    let atomic = Atomic::<f32>::new(-0.0);

    assert_ne!(0.0f32.to_bits(), (-0.0f32).to_bits());
    assert!(atomic.compare_set(0.0, 1.0).is_err());

    let prev = atomic.compare_and_exchange(0.0, 1.0);
    assert_eq!(prev, 0.0);
    assert_eq!(prev.to_bits(), (-0.0f32).to_bits());
    assert_eq!(atomic.load().to_bits(), (-0.0f32).to_bits());
}

#[test]
fn test_f64_compare_exchange_uses_raw_bits() {
    let atomic = Atomic::<f64>::new(-0.0);

    assert_ne!(0.0f64.to_bits(), (-0.0f64).to_bits());
    assert!(atomic.compare_set(0.0, 1.0).is_err());

    let prev = atomic.compare_and_exchange(0.0, 1.0);
    assert_eq!(prev, 0.0);
    assert_eq!(prev.to_bits(), (-0.0f64).to_bits());
    assert_eq!(atomic.load().to_bits(), (-0.0f64).to_bits());
}

#[test]
fn test_inner_backends() {
    let flag = Atomic::<bool>::new(false);
    assert!(!flag.inner().load(Ordering::Acquire));

    let number = Atomic::<i32>::new(42);
    assert_eq!(number.inner().load(Ordering::Acquire), 42);
}