# Mutexes

### Mutexes

A mutex is a lock for shared data.

Only one thread may hold a mutex at a time. If another thread tries to lock it, that thread waits.

A mutex is used when several threads must read or change the same value.

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

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

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

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

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

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

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

The important part is this:

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

This section is protected. While one thread is inside it, the other thread cannot enter.

The protected section should be as small as possible.

This is better:

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

This is worse:

```zig
mutex.lock();

var i: u32 = 0;
while (i < 100000) : (i += 1) {
    counter += 1;
}

mutex.unlock();
```

The second version holds the mutex for too long. Other threads must wait even though the loop could have done most of its work without holding the lock.

A common pattern is to use `defer`:

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

counter += 1;
```

`defer` runs when the current scope exits. This prevents forgotten unlocks.

Use it when the protected block has several exits:

```zig
fn add(value: u32) void {
    mutex.lock();
    defer mutex.unlock();

    counter += value;
}
```

The lock is released even if the function returns early.

A mutex protects data, not code. The programmer decides which data belongs to the mutex.

For example:

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

    fn add(self: *State, n: u32) void {
        self.mutex.lock();
        defer self.mutex.unlock();

        self.count += n;
    }
};
```

Here the mutex and the data are stored together. This is usually clearer than having global locks and global data in separate places.

Use it like this:

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

const State = struct {
    mutex: std.Thread.Mutex = .{},
    count: u32 = 0,

    fn add(self: *State, n: u32) void {
        self.mutex.lock();
        defer self.mutex.unlock();

        self.count += n;
    }
};

fn worker(state: *State) void {
    var i: u32 = 0;

    while (i < 100000) : (i += 1) {
        state.add(1);
    }
}

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

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

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

    std.debug.print("count = {d}\n", .{state.count});
}
```

This program passes a pointer to shared state into each thread.

The mutex is inside the state. Every function that changes `count` must lock the mutex first.

Do not read shared mutable data without the same lock.

This is wrong:

```zig
std.debug.print("count = {d}\n", .{state.count});
```

if another thread may still be changing `state.count`.

This is safe after both threads have joined, because no worker thread is still running.

A mutex can also protect several fields:

```zig
const State = struct {
    mutex: std.Thread.Mutex = .{},
    items_done: u32 = 0,
    bytes_read: u64 = 0,
    failed: bool = false,
};
```

All fields protected by the mutex must follow the same rule: lock before access.

Mutexes can cause deadlock.

A deadlock occurs when threads wait forever for locks that cannot be released.

A simple case:

```zig
var a = std.Thread.Mutex{};
var b = std.Thread.Mutex{};
```

Thread 1 does:

```zig
a.lock();
b.lock();
```

Thread 2 does:

```zig
b.lock();
a.lock();
```

Each thread may hold one mutex and wait for the other. Neither can continue.

The usual rule is simple: always take locks in the same order.

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

b.lock();
defer b.unlock();
```

Every thread that needs both locks should lock `a` before `b`.

Mutexes are useful, but they are not a complete design. They are a tool for small, clear regions of shared state.

A good threaded program tries to reduce sharing. When sharing is necessary, it protects the shared data with a clear owner and a small lock boundary.

Exercise 18-5. Rewrite the global counter example so the mutex and counter are fields of a struct.

Exercise 18-6. Add a `get` method to the struct that returns the current count safely.

Exercise 18-7. Write two mutexes and two worker functions. Make both workers lock the mutexes in the same order.

Exercise 18-8. Move expensive work outside the locked section, then measure the difference.

