Impossible OS Docs

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?

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.

See also

Impossible OS is free software under the GPL-3.0-only license. Last updated Edit this page on GitHub