Synchronization Primitives

Compare-and-Swap

Compare-and-Swap (CAS) is the foundational atomic read-modify-write primitive for lock-free algorithms. It compares an atomic variable's current value against an expected value and, if and only if they are equal, replaces the variable's value with new in a single indivisible hardware step, returning Result<T, T> containing the value observed before the operation.

compare_exchange vs. compare_exchange_weak

Rust's atomic types provide two compare-and-swap methods (plus the closure-based .fetch_update() helper):

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

pub fn compare_exchange_weak(
    &self,
    current: T,
    new: T,
    success: Ordering,
    failure: Ordering,
) -> Result<T, T>;
Method Can Fail Spuriously When *self == current? Best Used When
compare_exchange No (Err(actual) is returned only when actual != current) Single-shot state transitions (if state.compare_exchange(IDLE, RUNNING, ...).is_ok())
compare_exchange_weak Yes (on LL/SC architectures like ARM/RISC-V, a cache-line interruption can cause Err(actual) even when actual == current) Inside a retry loop (while let Err(actual) = ...), where it compiles to a single fast LL/SC pair on ARM instead of a nested hardware loop!
fetch_update(set_order, fetch_order, f) Wraps a compare_exchange_weak loop automatically Computing a new value Option<T> as a pure function of the current value
CAS Retry Loop (`compare_exchange_weak`):

  let mut cur = atomic.load(Ordering::Relaxed);
  loop {
      let next = compute_from(cur);
      match atomic.compare_exchange_weak(cur, next, Ordering::AcqRel, Ordering::Relaxed) {
          Ok(prev) => break prev,       // Succeeded! `atomic` is now `next`
          Err(actual) => cur = actual,  // Another thread modified `atomic` (or spurious fail);
      }                                 // retry immediately with the fresh `actual` value!
  }

Implementing Conditional Saturating Subtraction with a CAS Loop and fetch_update

Because hardware does not provide a single instruction for "subtract amount only if balance >= amount, otherwise leave unchanged", this operation is implemented using either a compare_exchange_weak loop or .fetch_update(...):

use std::sync::atomic::{AtomicU64, Ordering};
use std::thread;

fn try_withdraw_cas(balance: &AtomicU64, amount: u64) -> Result<u64, u64> {
    let mut current = balance.load(Ordering::Relaxed);
    loop {
        if current < amount {
            return Err(current);
        }
        let next = current - amount;
        match balance.compare_exchange_weak(
            current,
            next,
            Ordering::AcqRel,
            Ordering::Relaxed,
        ) {
            Ok(_) => return Ok(next),
            Err(actual) => current = actual,
        }
    }
}

fn multiply_capped(value: &AtomicU64, factor: u64, cap: u64) -> u64 {
    // `.fetch_update()` runs the `compare_exchange_weak` loop automatically:
    value
        .fetch_update(Ordering::AcqRel, Ordering::Relaxed, |cur| {
            Some((cur * factor).min(cap))
        })
        .unwrap()
}

fn main() {
    let account = AtomicU64::new(250);

    thread::scope(|s| {
        for _ in 0..4 {
            s.spawn(|| {
                let _ = try_withdraw_cas(&account, 100);
            });
        }
    });

    // Only two 100-unit withdrawals can succeed out of 250 -> 50 remains!
    println!("remaining balance after 4x100 attempts: {}", account.load(Ordering::Relaxed));

    multiply_capped(&account, 10, 300);
    println!("after capped multiply:                  {}", account.load(Ordering::Relaxed));
}
remaining balance after 4x100 attempts: 50
after capped multiply:                  300