← All system designs

Concurrency

Threads, the synchronization primitives (locks, condition variables, semaphores), and the bugs that come with shared state.

8 items 2 Foundational 5 Intermediate 1 Advanced

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 if instead of while around 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
Search ESC

Keyboard shortcuts

Shortcuts are disabled while typing in inputs.