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
| Choice | Gain | Cost |
|---|---|---|
| unsynchronized increment | minimal code | incorrect totals |
| mutex | simple correctness | contention |
| atomic or sharded counters | better scalability | added 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
| Choice | Gain | Cost |
|---|---|---|
if before wait | shorter code | broken on wake races |
while on predicate | correct synchronization | slightly more ceremony |
| semaphore-only redesign | simpler queue contract in some cases | different 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
| Choice | Gain | Cost |
|---|---|---|
| assume every lock traps to kernel | easy mental model | wrong performance diagnosis |
| understand futex fast path | accurate profiling | more systems detail |
| redesign to reduce contention | fewer slow paths | implementation 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
| Choice | Gain | Cost |
|---|---|---|
| ad hoc lock order | simple local code | deadlock risk |
| global lock ordering | eliminates circular wait | coordination across code |
| coarse single lock | easy reasoning | less 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
| Choice | Gain | Cost |
|---|---|---|
| spin around blocking I/O | simple primitive reuse | wasted CPU |
| blocking mutex | scheduler-friendly wait | context-switch overhead |
| queue and async logging | decoupled latency | more 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
| Source | Use it for |
|---|---|
| pthread_mutex_lock(3p) | mutex behavior |
| pthread condition variables | condition 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.