# Atomics

### Atomics

An atomic operation is a small operation that can safely happen while several threads are running.

Atomics are used for simple shared values, such as counters, flags, indexes, and state markers. They are lower-level than mutexes. They can be faster in some cases, but they are also easier to misuse.

Use atomics when the shared operation is small and independent.

Use a mutex when several pieces of data must be protected together.

#### The Problem Atomics Solve

Suppose two threads update the same counter:

```zig
counter += 1;
```

This looks like one operation, but it is not. It usually means:

```text
read counter
add 1
write counter
```

Two threads can interleave these steps and lose an update.

An atomic increment makes the whole update safe as one indivisible operation.

#### Atomic Values in Zig

In Zig, you can use `std.atomic.Value` to create an atomic value.

```zig
const std = @import("std");

var counter = std.atomic.Value(u32).init(0);
```

This creates an atomic `u32` initialized to `0`.

To read the value:

```zig
const value = counter.load(.seq_cst);
```

To write the value:

```zig
counter.store(10, .seq_cst);
```

To add to the value:

```zig
_ = counter.fetchAdd(1, .seq_cst);
```

The `.seq_cst` argument is the memory ordering. For now, read it as “use the safest and easiest-to-understand ordering.”

#### Full Atomic Counter Example

```zig
const std = @import("std");

var counter = std.atomic.Value(u32).init(0);

fn incrementMany() void {
    var i: u32 = 0;

    while (i < 1000) : (i += 1) {
        _ = counter.fetchAdd(1, .seq_cst);
    }
}

pub fn main() !void {
    const t1 = try std.Thread.spawn(.{}, incrementMany, .{});
    const t2 = try std.Thread.spawn(.{}, incrementMany, .{});

    t1.join();
    t2.join();

    const final = counter.load(.seq_cst);
    std.debug.print("counter = {}\n", .{final});
}
```

This should print:

```text
counter = 2000
```

The important line is:

```zig
_ = counter.fetchAdd(1, .seq_cst);
```

`fetchAdd` adds `1` to the value atomically.

It also returns the old value. In this example, we do not need the old value, so we assign it to `_`.

#### Atomic Load

A load reads an atomic value.

```zig
const value = counter.load(.seq_cst);
```

Use `load` instead of reading the value directly.

This is correct:

```zig
const value = counter.load(.seq_cst);
```

This is not the intended style:

```zig
const value = counter;
```

The atomic object is not the plain integer. It is a wrapper that controls safe access to the integer.

#### Atomic Store

A store writes an atomic value.

```zig
counter.store(0, .seq_cst);
```

This sets the counter to `0`.

A store is useful for flags:

```zig
const std = @import("std");

var should_stop = std.atomic.Value(bool).init(false);

fn worker() void {
    while (!should_stop.load(.seq_cst)) {
        doWork();
    }
}

fn doWork() void {
    // work here
}
```

Another thread can later stop the worker:

```zig
should_stop.store(true, .seq_cst);
```

The worker will eventually observe the new value and exit its loop.

#### Atomic Fetch Operations

A fetch operation changes the value and returns the previous value.

Common operations include:

| Operation | Meaning |
|---|---|
| `fetchAdd` | Add a value |
| `fetchSub` | Subtract a value |
| `fetchAnd` | Bitwise AND |
| `fetchOr` | Bitwise OR |
| `fetchXor` | Bitwise XOR |
| `swap` | Replace the value and return the old value |

Example:

```zig
const old = counter.fetchAdd(1, .seq_cst);
```

If `counter` was `5`, this operation makes it `6` and returns `5`.

#### Atomic Flags

A common use of atomics is a shared flag.

```zig
const std = @import("std");

const State = struct {
    stop: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
};

fn worker(state: *State) void {
    while (!state.stop.load(.seq_cst)) {
        std.debug.print("working\n", .{});
        std.time.sleep(100 * std.time.ns_per_ms);
    }

    std.debug.print("worker stopped\n", .{});
}

pub fn main() !void {
    var state = State{};

    const thread = try std.Thread.spawn(.{}, worker, .{&state});

    std.time.sleep(500 * std.time.ns_per_ms);
    state.stop.store(true, .seq_cst);

    thread.join();
}
```

The main thread owns the decision to stop.

The worker thread checks the flag.

The flag is atomic because one thread writes it while another thread reads it.

#### Atomics Do Not Protect Groups of Data

This is the main beginner mistake.

Atomics protect one operation on one value. They do not automatically protect a larger relationship.

Suppose you have:

```zig
var x = std.atomic.Value(u32).init(0);
var y = std.atomic.Value(u32).init(0);
```

One thread does:

```zig
x.store(10, .seq_cst);
y.store(20, .seq_cst);
```

Another thread does:

```zig
const a = x.load(.seq_cst);
const b = y.load(.seq_cst);
```

The second thread might see `x = 10` and `y = 0` if it reads between the two stores.

Each individual operation is atomic. The group is not atomic.

If `x` and `y` must be updated together, use a mutex.

#### Atomics vs Mutexes

Use this rule first:

| Need | Use |
|---|---|
| Protect one simple counter | Atomic |
| Protect one stop flag | Atomic |
| Protect several fields together | Mutex |
| Protect a data structure | Mutex |
| Maintain an invariant | Mutex |
| Coordinate complex behavior | Mutex or condition variable |

Atomics are not a replacement for mutexes. They solve a narrower problem.

#### Memory Ordering

Every atomic operation in Zig asks for a memory ordering:

```zig
counter.load(.seq_cst);
counter.store(1, .seq_cst);
counter.fetchAdd(1, .seq_cst);
```

Memory ordering controls how atomic operations are seen by different threads and by the compiler.

For beginners, use:

```zig
.seq_cst
```

It means sequential consistency.

Sequential consistency gives the simplest mental model: all threads observe atomic operations in one consistent order.

There are weaker orderings, such as acquire, release, and relaxed. They can be faster in some cases, but they require much more care. Use them only when you understand the memory model and have measured a real need.

#### Atomic Counter as a Struct

A clean style is to hide the atomic operations behind methods.

```zig
const std = @import("std");

const Counter = struct {
    value: std.atomic.Value(u64) = std.atomic.Value(u64).init(0),

    fn increment(self: *Counter) void {
        _ = self.value.fetchAdd(1, .seq_cst);
    }

    fn get(self: *Counter) u64 {
        return self.value.load(.seq_cst);
    }
};
```

Now the rest of the code does not need to know the exact atomic operation.

```zig
counter.increment();
const n = counter.get();
```

This keeps the atomic policy in one place.

#### Compare and Swap

Some atomic code needs to update a value only if it still has an expected old value.

This is called compare and swap, often shortened to CAS.

The idea is:

```text
If the value is still old_value, replace it with new_value.
Otherwise, report that the value changed.
```

This is useful for lock-free algorithms, state machines, and advanced concurrent data structures.

A simplified shape looks like this:

```zig
_ = value.cmpxchgStrong(
    expected,
    new_value,
    .seq_cst,
    .seq_cst,
);
```

Beginners do not need to write CAS-heavy code at first. It is enough to know that it exists and that it is more advanced than `load`, `store`, and `fetchAdd`.

#### A Practical Example: Assigning Work IDs

Atomics are useful when many threads need unique numbers.

```zig
const std = @import("std");

const WorkIds = struct {
    next_id: std.atomic.Value(u64) = std.atomic.Value(u64).init(1),

    fn next(self: *WorkIds) u64 {
        return self.next_id.fetchAdd(1, .seq_cst);
    }
};

fn worker(ids: *WorkIds) void {
    const id = ids.next();
    std.debug.print("work id = {}\n", .{id});
}

pub fn main() !void {
    var ids = WorkIds{};

    const t1 = try std.Thread.spawn(.{}, worker, .{&ids});
    const t2 = try std.Thread.spawn(.{}, worker, .{&ids});
    const t3 = try std.Thread.spawn(.{}, worker, .{&ids});

    t1.join();
    t2.join();
    t3.join();
}
```

Each worker receives a different ID.

The output order may vary:

```text
work id = 2
work id = 1
work id = 3
```

But the IDs are unique because `fetchAdd` is atomic.

#### When Atomics Are a Bad Fit

Avoid atomics when the code starts becoming hard to explain.

This is usually a warning sign:

```text
load x
load y
check state
compare exchange flag
store x
fetch add y
retry loop
```

At that point, a mutex may be clearer and safer.

Lock-free code can be valuable, but it is difficult to get right. For most application code, a simple mutex is often better.

#### The Main Rule

Atomics are for small shared facts.

A counter is a small shared fact.

A stop flag is a small shared fact.

A work ID generator is a small shared fact.

A hash map is not a small shared fact. A queue is not a small shared fact. A bank account with several related fields is not a small shared fact.

For those, use a mutex or a higher-level synchronization structure.

Atomics are sharp tools. Use them when the operation is simple, the shared state is tiny, and the correctness rule is easy to state.

