Concurrency
Threads, the synchronization primitives (locks, condition variables, semaphores), and the bugs that come with shared state.
Concurrency is the part of OS knowledge that follows you into every job, regardless of whether you ever write kernel code. Once two threads share state, you need primitives that make access atomic — and those primitives have failure modes (deadlock, starvation, livelock, priority inversion) that are easy to ship and hard to debug.
The canonical primitives are locks, condition variables, and semaphores. Know what each one is for and when to reach for which. Lock-free code is interesting but rarely the right answer for a first pass.
Key concepts
- The problem isn't concurrency, it's uncontrolled scheduling — the OS can preempt anywhere
- Atomicity is what every synchronization primitive provides — you compose programs out of atomic regions
- Condition variables solve waiting; semaphores solve counting; locks solve mutual exclusion
- Always use `while (!condition) cv.wait()` — never `if` — to handle spurious wakeups
- Deadlock has four conditions; remove any one and you've prevented it
Reference template
// Choosing a primitive
1. Need mutual exclusion? → lock (mutex)
2. Need to wait for a condition? → condition variable on a lock
3. Need to count resources? → counting semaphore
4. Need ordering between threads? → semaphore initialised to 0, or barrier
5. Need reader-writer fairness? → rwlock (or hand-roll with CV + counters)
6. Need lock-free? → atomic operations + memory ordering (last resort) Adapt to your problem; the structure is the load-bearing part.
Common pitfalls
- Using
ifinstead ofwhilearound a condition wait — spurious wakeups will bite you - Holding a lock across an I/O call or any unbounded-time operation
- Forgetting that signal() doesn't wake the signalled thread immediately — only when it's scheduled
- Reaching for lock-free code when a mutex would do — the bug surface is much larger
Related topics
Items (8)
- Threads and Shared State
Why threads, the heart of the problem (uncontrolled scheduling), atomicity, and the wish that motivates every primitive.
Concept Foundational - POSIX Threads API
pthread_create / join / mutex / cond — the canonical thread surface and the gotchas around using it correctly.
Building Block Foundational - Locks and Spinlocks
Test-and-set, compare-and-swap, fetch-and-add, two-phase locks, and why a spinlock is rarely the right answer in user space.
Building Block Intermediate - Concurrent Data Structures
Concurrent counters, linked lists, queues, and hash tables — coarse-grained vs hand-over-hand vs lock-free, and where each one breaks under contention.
Building Block Intermediate - Condition Variables
Wait / signal / broadcast, the always-use-while rule, the producer-consumer pattern, covering conditions.
Building Block Intermediate - Semaphores
Counting semaphores as a generalisation of locks; binary semaphores; ordering; readers-writers; dining philosophers.
Building Block Intermediate - Concurrency Bugs — Deadlock, Atomicity, Order
Non-deadlock vs deadlock bugs; the four conditions for deadlock; prevention, avoidance, detection-and-recovery.
Concept Intermediate - Event-Based Concurrency
Event loops, select / poll / epoll, async I/O, the manual state-management cost, and why JS, Nginx, and Node landed here.
Building Block Advanced