Skip to main content

Module 3: Concurrency & Synchronization: Case Studies

These case studies train schedule-level reasoning: races, locks, condition variables, futexes, deadlocks, and memory visibility.


Case Study 1: Lost Update In A Metrics Counter

Scenario: Eight threads increment a shared request counter. Under load, totals are lower than expected.

Source anchor: POSIX threads and CPU atomicity explain why read-modify-write needs synchronization. See pthread_mutex_lock(3p).

Module concepts: race condition, critical section, atomicity, mutex.

Wrong Approach

"Increment is one line, so it is one operation."

Better Approach

Protect or avoid shared mutation:

mutex:
simple correctness

atomic:
lower overhead for simple counter

per-thread counter:
reduced contention, merge later

Tradeoff Table

ChoiceGainCost
unsynchronized incrementminimal codeincorrect totals
mutexsimple correctnesscontention
atomic or sharded countersbetter scalabilityadded design work

Failure Mode

Two threads read the same counter value before either write becomes visible, so one increment overwrites the other.

Project / Capstone Connection

Use this in observability, rate-limiting, or analytics features where shared counters look simple but sit on hot concurrent paths.

Required Artifact

Draw the interleaving that loses one increment and write two fixes.


Case Study 2: Condition Variable Wait Without While Loop

Scenario: A worker waits for a queue to be non-empty using if, wakes spuriously or after another worker consumes the item, and pops from an empty queue.

Source anchor: man7 pthread_cond_init(3) explains condition variables and their associated mutex discipline.

Module concepts: condition variable, predicate, spurious wakeup, mutex.

Wrong Approach

Use if queue_empty wait().

Better Approach

Wait in a loop:

pthread_mutex_lock(&m);
while (queue_empty(q)) {
pthread_cond_wait(&cv, &m);
}
item = pop(q);
pthread_mutex_unlock(&m);

Tradeoff Table

ChoiceGainCost
if before waitshorter codebroken on wake races
while on predicatecorrect synchronizationslightly more ceremony
semaphore-only redesignsimpler queue contract in some casesdifferent primitive

Failure Mode

The thread wakes without the queue predicate being true, or another consumer wins the race first, so the awakened worker reads invalid state.

Project / Capstone Connection

This appears in producer-consumer queues, job schedulers, and background workers that capstone teams often build around shared buffers.

Required Artifact

Write the producer-consumer schedule that breaks if and the corrected loop.


Case Study 3: Futex Makes Uncontended Locks Cheap

Scenario: A learner assumes every mutex lock enters the kernel. Profiling shows uncontended locks are mostly user-space and only contended paths use kernel help.

Source anchor: futex(2) documents fast user-space locking with kernel assistance on contention.

Module concepts: futex, user-space fast path, kernel wait queue, contention.

Wrong Approach

"Locks are always syscalls."

Better Approach

Understand the fast/slow path:

uncontended:
atomic user-space state change

contended:
futex wait/wake through kernel

Tradeoff Table

ChoiceGainCost
assume every lock traps to kerneleasy mental modelwrong performance diagnosis
understand futex fast pathaccurate profilingmore systems detail
redesign to reduce contentionfewer slow pathsimplementation effort

Failure Mode

Teams misread profiling data, optimize the wrong layer, and miss that contention rather than lock existence is what drives kernel involvement.

Project / Capstone Connection

Use this when performance-tuning threaded services or runtimes where lock behavior shows up in profilers and needs correct interpretation.

Required Artifact

Draw mutex fast path and slow path with the futex operation point.


Case Study 4: Deadlock In Two-Resource Transfer

Scenario: Thread A locks account 1 then 2. Thread B locks account 2 then 1. Both wait forever.

Source anchor: POSIX mutex docs and classic OS deadlock conditions apply. See pthread_mutex_lock(3p).

Module concepts: deadlock, circular wait, lock ordering, wait-for graph.

Wrong Approach

"Deadlocks are rare; retry the program."

Better Approach

Use deterministic lock order:

lock min(account_id)
lock max(account_id)
perform transfer
unlock reverse order

Tradeoff Table

ChoiceGainCost
ad hoc lock ordersimple local codedeadlock risk
global lock orderingeliminates circular waitcoordination across code
coarse single lockeasy reasoningless parallelism

Failure Mode

Each thread holds one resource while waiting on the other, forming a circular wait that no scheduler decision can resolve.

Project / Capstone Connection

This maps to banking demos, inventory systems, or any capstone that transfers state between two shared entities.

Required Artifact

Draw the wait-for graph and rewrite the lock acquisition rule.


Case Study 5: Spinlock Used Around Blocking I/O

Scenario: A low-level service uses a spinlock while writing logs to disk. Under I/O delay, CPU burns while threads spin.

Source anchor: Linux synchronization primitives and OS scheduling distinguish spin waiting from blocking. Use futex(2) as the contrast point.

Module concepts: spinlock, blocking mutex, critical-section length, I/O.

Wrong Approach

"Spinlocks are faster."

Better Approach

Use spin only for tiny non-blocking critical sections:

short CPU-only state:
spin may fit

I/O or unknown wait:
blocking mutex/queue

Tradeoff Table

ChoiceGainCost
spin around blocking I/Osimple primitive reusewasted CPU
blocking mutexscheduler-friendly waitcontext-switch overhead
queue and async loggingdecoupled latencymore system design

Failure Mode

Threads burn CPU waiting for a lock that cannot be released quickly because the lock holder is blocked on disk I/O.

Project / Capstone Connection

Apply this to logging, persistence, or adapter layers where a capstone service mixes shared state protection with potentially slow external operations.

Required Artifact

Write a lock-choice table for five critical sections in a service.


Source Map

SourceUse it for
pthread_mutex_lock(3p)mutex behavior
pthread condition variablescondition variables and mutex association
futex(2)user-space fast path and kernel blocking

Completion Standard

  • At least three artifacts are completed.
  • At least one artifact includes a breaking interleaving.
  • At least one artifact includes a wait-for graph.