Synchronisation Primitives
What is it?
The locks and wait objects kernel code uses to coordinate threads and CPUs. The baseline set exists: IRQ-safe spinlocks, mutexes with priority inheritance, reader-writer locks, semaphores, condition variables, events, seqlocks, and the NT event, mutant and semaphore objects with NtWaitForMultipleObjects. The roadmap's additions (ticket locks, futexes, TLS, once-initialisation, barriers, cancellation, a unified wait vtable and timeouts on every primitive) are not built, and a known SMP defect in the mutex wait queue is still open.
How does it work?
Spinlocks. spinlock_t is a single word (spinlock.h). spin_lock_irqsave() disables interrupts and spins; spin_unlock_irqrestore() releases with the matching fence. A holder must never sleep, and a spinlock must never be held across mutex_lock() or sem_wait().
Mutexes. mutex_lock() in mutex.c tries a compare-and-swap on the lock word; on failure it adds the thread to a fixed wait list of MUTEX_MAX_WAITERS (16), boosts the direct owner's priority (only the direct owner: if that owner is itself blocked on another mutex, the thread it waits for is not boosted, despite a code comment saying otherwise), marks the thread blocked and yields. mutex_unlock() restores the owner's base priority and wakes a waiter. The wait list is updated without its own lock, and a seventeenth waiter is not queued at all; both are the subject of the roadmap's backfill section.
Events and waits. event_t (event.h) supports manual and auto reset; event_wait_timeout() is the only primitive with a timeout today. Semaphores, condition variables, reader-writer locks and seqlocks have their own headers in include/kernel/sched/.
NT objects. nt_sync.c exposes events, mutants and semaphores as Object Manager objects and implements NtWaitForSingleObject and NtWaitForMultipleObjects. The four keyed-event services are registered to a stub that returns STATUS_NOT_IMPLEMENTED.
What are its interfaces?
| Interface | Purpose |
|---|---|
spin_lock(), spin_lock_irqsave(), spin_trylock(), spin_is_locked() |
Spinlocks (spinlock.h) |
mutex_init(), mutex_lock(), mutex_unlock(), mutex_trylock() |
Sleeping mutex with priority inheritance (mutex.h) |
sem_init(), sem_wait(), sem_signal(), cond_wait(), cond_broadcast() |
Semaphores and condition variables |
event_wait_timeout(), event_try_consume() |
Kernel events (event.h) |
NtCreateEvent, NtCreateMutant, NtCreateSemaphore, NtWaitForMultipleObjects |
NT synchronisation objects (nt_sync.c) |
How do I use it?
Protect short, non-sleeping critical sections with spin_lock_irqsave(), and anything that may block with a mutex. Win32 code uses the NT objects through the usual CreateEvent, WaitForMultipleObjects family. The NT objects are tested in test_nt_sync.c, and the kernel mutex and semaphore in test_ipc.c:
bash scripts/test.sh SUITE=abi
bash scripts/test.sh SUITE=sched
bash scripts/test.sh SUITE=ipc
What is not implemented yet?
- Ticket locks for FIFO-fair spinning (Ticket Locks).
- A preemption count API. The per-CPU field exists;
preempt_disable()andpreempt_enable()do not (Preemption Count). - Thread-local storage and futexes (Thread-Local Storage, Futexes).
- Once-initialisation, barriers and cancellation (
pthread_once,pthread_barrier_t, Thread Cancellation). - One wait vtable across every primitive, and timeouts everywhere (
WaitForMultipleObjects(waitable_tvtable), Unified_timeout(ms)API). - Keyed events and thread alerts (Sync Syscalls Wired to SSDT).
- A locked, unbounded mutex wait queue (Mutex Wait-Queue SMP-Safety Backfill).
How does it compare with Windows 11 and Linux?
Windows 11 has queued spinlocks, keyed events, WaitOnAddress, TLS and WaitForMultipleObjects over every dispatcher object; Linux has queued spinlocks, futex(2), TLS, pthread_barrier_t and cancellation. Impossible OS has the classic primitive set, priority inheritance and the NT wait objects, but none of the roadmap's rows yet. Its planned additions are one wait interface that every primitive implements and a kernel-level barrier object.