Atomic Operation

An atomic operation is indivisible: other threads see the state from before or after it runs, never partway through.

Hardware supports them:

They are a lower-overhead alternative to locks when the operation fits in one instruction.

Why use atomics instead of a lock?

Locks have memory, init, and acquire/release cost, plus deadlock risk. If the whole mutation is a single read-modify-write on one word, an atomic skips all of that.

Covered in ECE459 L12. Rust’s atomics cover integer types (signed and unsigned), bool, size, and pointers. You can’t just read or write the value; you must use load and store so atomicity is explicit. The types are Send + Sync, safe to pass and share across threads.

use std::sync::atomic::{AtomicBool, Ordering};
let b = AtomicBool::new(false);
b.store(true, Ordering::SeqCst);
println!("{}", b.load(Ordering::SeqCst));

In ECE459 we only discuss sequential consistency (Ordering::SeqCst). Other memory orderings come later.

Read-modify-write helpers: fetch_add, fetch_sub, fetch_max, fetch_min, fetch_and, fetch_nand, fetch_or, fetch_xor. C’s count++ is not atomic; count.fetch_add(1, Ordering::SeqCst) is.

Atomics don't solve everything

  • Atomicity of a single read or write doesn’t prevent higher-level race conditions
  • Not every atomic op is portable. Rust may emulate (e.g. AtomicI8 backed by a 4-byte type) or fail on some platforms