Skip to content

std.sync

locks, condvars, queues, channels, wait groups; every type is #sync share by address; never copy, move or free one while a thread uses it (join first)

pub Mutex :: struct {
word: u64,
}

zero value is unlocked; no init, no destroy; not recursive, not fair

  • word: OS lock state; all zero bits is the unlocked state
pub lock :: fn(self: *Self)

sleeps until free; relocking from the same thread deadlocks

pub try_lock :: fn(self: *Self) -> bool

take it only if free now; true = caller holds it

pub unlock :: fn(self: *Self)

caller must hold it; unlocking one it does not hold is UB

pub CondVar :: struct {
word: u64,
}

wait() may wake spuriously: loop on the condition with the lock held signal/broadcast work with or without the lock; a signal with no waiter is lost

  • word: OS state; zero = nobody waiting
pub wait :: fn(self: *Self, m: *Mutex)

release m, sleep, retake m; caller holds m, same Mutex for every waiter

pub wait_timeout :: fn(self: *Self, m: *Mutex, ms: u32) -> bool

like wait(), but false after ms ms (0xFFFFFFFF = never); lock retaken either way

pub signal :: fn(self: *Self)

wake one waiter, if any

pub broadcast :: fn(self: *Self)

wake every waiter

pub Queue :: struct($T: type) {
mu: Mutex,
not_empty: CondVar,
not_full: CondVar,
buf: []T,
head: usize,
count: usize,
closed: bool,
}

bounded blocking MPMC FIFO over caller storage; no alloc, nothing to destroy after close(): push false, pop drains then null; free storage only after joining

  • buf: the caller’s storage; capacity == buf.len
  • head: index of the oldest item
  • count: items queued
pub push :: fn(self: *Self, item: T) -> bool

sleeps while full; false if closed or zero capacity (item not added)

pub try_push :: fn(self: *Self, item: T) -> bool

push only if room now; false if full, closed or zero capacity

pub pop :: fn(self: *Self) -> ?T

pop the oldest, sleeping while empty; null once closed and drained

pub try_pop :: fn(self: *Self) -> ?T

pop the oldest if any now; null if empty

pub close :: fn(self: *Self)

end the queue, wake all blocked producers and consumers; idempotent

pub len :: fn(self: *Self) -> usize

items queued now; a snapshot

pub capacity :: fn(self: *Self) -> usize
pub is_closed :: fn(self: *Self) -> bool
pub make_queue :: fn($T: type, storage: []T) -> Queue(T)

queue over storage; it must outlive all users and nothing else may touch it

pub Channel :: struct($T: type) {
queue: Queue(T),
}

Queue with its own storage; share by pointer, don’t move it while threads use it after close(): send gives false, recv drains then null

pub send :: fn(self: *Self, item: T) -> bool

waits while full; false if closed

pub try_send :: fn(self: *Self, item: T) -> bool

send if room now; false if full or closed

pub recv :: fn(self: *Self) -> ?T

waits while empty; null once closed and empty

pub try_recv :: fn(self: *Self) -> ?T

next item if any now, else null

pub close :: fn(self: *Self)

no more sends; wakes all waiters; idempotent

pub len :: fn(self: *Self) -> usize

items now; a snapshot

pub capacity :: fn(self: *Self) -> usize
pub is_closed :: fn(self: *Self) -> bool
pub channel :: fn($T: type, a: *Arena, capacity: usize) -> Channel(T)

channel of capacity items; storage in a

pub WaitGroup :: struct {
mu: Mutex,
cv: CondVar,
pending: usize,
}

add(n) before the tasks start, done() in each, wait() sleeps until all are done

pub add :: fn(self: *Self, n: usize)

n more tasks to wait for

pub done :: fn(self: *Self)

one task done; the last one wakes every waiter

pub wait :: fn(self: *Self)

sleep until every added task called done

pub Once :: struct {
mu: Mutex,
done: u32,
}

run something exactly once across threads; later callers wait for it to finish

  • done: 1 once the function has returned
pub call :: fn(self: *Self, f: fn())

run f unless already run; every caller returns after f has returned

pub is_done :: fn(self: *Self) -> bool
pub RwLock :: struct {
mu: Mutex,
readers_go: CondVar,
writer_go: CondVar,
readers: usize,
writer: bool,
writers_waiting: usize,
}

many readers or one writer; a waiting writer blocks new readers; not recursive

  • readers: threads that hold it shared
  • writer: a thread holds it exclusively
pub lock_shared :: fn(self: *Self)
pub unlock_shared :: fn(self: *Self)
pub lock :: fn(self: *Self)
pub unlock :: fn(self: *Self)
pub Semaphore :: struct {
mu: Mutex,
cv: CondVar,
permits: usize,
}

at most n threads at once

pub acquire :: fn(self: *Self)

take a permit, sleeping until one is free

pub try_acquire :: fn(self: *Self) -> bool

take a permit only if one is free now

pub release :: fn(self: *Self)

give a permit back, wake one waiter

pub semaphore :: fn(permits: usize) -> Semaphore

semaphore with permits permits; keep it in place, share by address