Skip to content
📖 Auto vs Modern CBook contents

Chapter 20: Threads ​

Level 3 -- Experience

Shared-memory concurrency with C threads, mutexes, and condition variables -- and why Auto's actor model makes most of this unnecessary.

Concurrency is where C programs most often go wrong. Data races, deadlocks, and subtle memory ordering bugs are notoriously difficult to reproduce and fix. C11 introduced a standard threading API based on POSIX threads. Auto takes a fundamentally different approach: actors with isolated state communicating through message passing.


20.1 Simple Interthread Control ​

C11 provides thrd_create and thrd_join for thread management:

c
// C Deep Dive: thread creation
#include <threads.h>
#include <stdio.h>

int worker(void *arg) {
    int id = *(int *)arg;
    printf("Worker %d started\n", id);
    int sum = 0;
    for (int i = 0; i < 100; i++) {
        sum += i;
    }
    printf("Worker %d finished, sum = %d\n", id, sum);
    return 0;
}

int main(void) {
    thrd_t t1, t2;
    int id1 = 1, id2 = 2;

    thrd_create(&t1, worker, &id1);
    thrd_create(&t2, worker, &id2);

    thrd_join(t1, NULL);
    thrd_join(t2, NULL);
    return 0;
}

C Deep Dive: C threads share the entire address space. Every thread can read and write every global variable. This makes data races trivially easy to create. The thread function must return int (not void* like POSIX). The thrd_create argument is passed as void* -- common source of type errors.

Auto's approach. Auto uses the actor model instead of raw threads:

auto
// Auto: sequential execution (actor model is planned)
fn worker(id int) {
    print("Worker", id, "started")
    var sum int = 0
    for i in 0..100 {
        sum = sum + i
    }
    print("Worker", id, "finished, sum =", sum)
}

fn main() {
    worker(1)
    worker(2)
}

Released under the MIT License.