# Mutexes

### Mutexes

A mutex is a lock for shared data.

The word “mutex” means “mutual exclusion.” That means only one thread is allowed to enter a protected section of code at a time.

You use a mutex when several threads need to access the same data, and at least one of them may change it.

#### Why Mutexes Exist

This code looks harmless:

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

But it is not one simple operation at the machine level. It is closer to this:

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

If two threads do this at the same time, they can both read the same old value and both write back the same new value.

For example, suppose `counter` starts at `10`.

```text
Thread A reads 10
Thread B reads 10
Thread A writes 11
Thread B writes 11
```

Two increments happened, but the final value is only `11`.

The correct value should be `12`.

A mutex prevents this by allowing only one thread to update the counter at a time.

#### A Basic Mutex

In Zig, a mutex is available as `std.Thread.Mutex`.

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

var mutex = std.Thread.Mutex{};
var counter: u32 = 0;
```

To protect shared data, lock the mutex before using the data, then unlock it afterward.

```zig
mutex.lock();
counter += 1;
mutex.unlock();
```

Only one thread can hold the mutex at a time.

If another thread reaches `mutex.lock()` while the mutex is already locked, it waits.

#### Full Example

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

var mutex = std.Thread.Mutex{};
var counter: u32 = 0;

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

    while (i < 1000) : (i += 1) {
        mutex.lock();
        counter += 1;
        mutex.unlock();
    }
}

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

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

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

This program starts two threads. Each thread increments the same `counter` 1000 times.

Without the mutex, the final value may be wrong.

With the mutex, the final value should be:

```text
counter = 2000
```

The mutex protects this critical section:

```zig
mutex.lock();
counter += 1;
mutex.unlock();
```

A critical section is code that must not be executed by multiple threads at the same time.

#### Use `defer` to Unlock Safely

Writing `lock` and `unlock` manually can be dangerous.

This code has a problem:

```zig
mutex.lock();

if (someCondition()) {
    return;
}

counter += 1;
mutex.unlock();
```

If `someCondition()` is true, the function returns before calling `mutex.unlock()`.

The mutex stays locked forever. Other threads will wait forever.

This is called a deadlock.

Use `defer` to make the unlock automatic at the end of the scope:

```zig
mutex.lock();
defer mutex.unlock();

if (someCondition()) {
    return;
}

counter += 1;
```

Now `mutex.unlock()` runs when the scope exits, even if the function returns early.

This is the standard style:

```zig
mutex.lock();
defer mutex.unlock();

// use protected data here
```

#### Keep the Locked Section Small

A mutex blocks other threads while it is locked.

So this is bad:

```zig
mutex.lock();
defer mutex.unlock();

counter += 1;
expensiveCalculation();
writeLargeFile();
```

The mutex protects `counter`, but it also blocks other threads while the calculation and file write happen.

Better:

```zig
mutex.lock();
counter += 1;
mutex.unlock();

expensiveCalculation();
writeLargeFile();
```

Lock only around the shared data.

The locked section should be as small as possible while still being correct.

#### Protect Data, Not Code

A mutex should be associated with the data it protects.

This style is unclear:

```zig
var global_mutex = std.Thread.Mutex{};
var counter: u32 = 0;
var total: u32 = 0;
var current_name: []const u8 = "";
```

Which data does `global_mutex` protect?

Maybe all of it. Maybe only some of it. The code does not say.

A better style is to group the mutex with the data:

```zig
const Counter = struct {
    mutex: std.Thread.Mutex = .{},
    value: u32 = 0,

    fn increment(self: *Counter) void {
        self.mutex.lock();
        defer self.mutex.unlock();

        self.value += 1;
    }

    fn get(self: *Counter) u32 {
        self.mutex.lock();
        defer self.mutex.unlock();

        return self.value;
    }
};
```

Now the relationship is clear.

The mutex protects `value`.

The methods are responsible for locking and unlocking.

#### Using the Counter

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

const Counter = struct {
    mutex: std.Thread.Mutex = .{},
    value: u32 = 0,

    fn increment(self: *Counter) void {
        self.mutex.lock();
        defer self.mutex.unlock();

        self.value += 1;
    }

    fn get(self: *Counter) u32 {
        self.mutex.lock();
        defer self.mutex.unlock();

        return self.value;
    }
};

fn worker(counter: *Counter) void {
    var i: u32 = 0;

    while (i < 1000) : (i += 1) {
        counter.increment();
    }
}

pub fn main() !void {
    var counter = Counter{};

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

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

    std.debug.print("counter = {}\n", .{counter.get()});
}
```

This is cleaner than using global variables.

The shared state is explicit:

```zig
var counter = Counter{};
```

Each worker receives a pointer to the same counter:

```zig
.{&counter}
```

The counter itself decides how locking works.

#### Do Not Read Shared Data Without Locking

A common mistake is locking only when writing.

```zig
mutex.lock();
counter += 1;
mutex.unlock();

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

The print reads `counter` without holding the mutex.

That may still be a data race if another thread writes at the same time.

Use the mutex for both reads and writes:

```zig
mutex.lock();
defer mutex.unlock();

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

Or hide the access behind methods:

```zig
const value = counter.get();
std.debug.print("counter = {}\n", .{value});
```

The rule is simple:

Every access to shared mutable data must follow the same locking rule.

#### Deadlock

A deadlock happens when threads wait forever.

The simplest deadlock is forgetting to unlock:

```zig
mutex.lock();
return;
mutex.unlock();
```

The `unlock` line never runs.

Another common deadlock happens when two locks are taken in different orders.

```text
Thread A locks mutex1
Thread B locks mutex2
Thread A waits for mutex2
Thread B waits for mutex1
```

Both threads are waiting. Neither can continue.

To avoid this, use a fixed lock order.

For example:

```text
Always lock user_mutex before account_mutex.
Never lock account_mutex before user_mutex.
```

This rule matters more as programs get larger.

#### Avoid Calling Unknown Code While Holding a Mutex

Be careful with this pattern:

```zig
mutex.lock();
defer mutex.unlock();

callback();
```

The function `callback` may do something unexpected. It may try to lock the same mutex. It may block for a long time. It may call code that calls back into your object.

A safer pattern is:

```zig
mutex.lock();
const snapshot = value;
mutex.unlock();

callback(snapshot);
```

Copy the needed data while holding the lock, then release the lock before calling unknown code.

#### Mutexes and Performance

A mutex has a cost.

Locking and unlocking are not free. More importantly, a mutex can make threads wait.

If many threads constantly fight for the same mutex, the program may become slower than a single-threaded version.

This problem is called contention.

High contention means many threads want the same lock at the same time.

To reduce contention:

| Technique | Meaning |
|---|---|
| Smaller critical sections | Hold the lock for less time |
| Separate locks | Use different mutexes for unrelated data |
| Thread-local data | Let each thread work on its own data |
| Batch updates | Lock once, apply many changes, unlock |
| Atomics | Use atomic operations for simple shared values |

Do not start with clever locking. Start with correct locking. Then measure.

#### Mutexes vs Atomics

A mutex is good when you need to protect a group of operations.

For example:

```zig
balance -= amount;
transactions.append(transaction);
last_updated = now;
```

These operations belong together. A mutex is a natural fit.

An atomic is better for very small shared values, such as a simple counter or flag. Atomics are covered later.

For beginners, use mutexes first. They are easier to reason about.

#### The Main Rule

A mutex protects an invariant.

An invariant is something that should always remain true.

For example, suppose you have this state:

```zig
const Account = struct {
    balance: i64,
    transaction_count: u64,
};
```

You may want this rule:

```text
Whenever balance changes, transaction_count must also change.
```

Then both fields should be protected by the same mutex:

```zig
const Account = struct {
    mutex: std.Thread.Mutex = .{},
    balance: i64 = 0,
    transaction_count: u64 = 0,

    fn deposit(self: *Account, amount: i64) void {
        self.mutex.lock();
        defer self.mutex.unlock();

        self.balance += amount;
        self.transaction_count += 1;
    }
};
```

The mutex protects the relationship between the fields, not just the fields themselves.

#### Good Mutex Style

A good Zig mutex pattern looks like this:

```zig
const Thing = struct {
    mutex: std.Thread.Mutex = .{},
    value: u32 = 0,

    fn update(self: *Thing) void {
        self.mutex.lock();
        defer self.mutex.unlock();

        self.value += 1;
    }
};
```

The data and mutex live together.

The method locks before touching the data.

The method uses `defer` to unlock.

The locked section is small.

That is the basic discipline of mutexes.

