Thread A and Thread B both execute the sequence: (1) read lock variable — see it is free, (2) set lock variable to 'taken'. What concurrency problem does this expose?
ABoth threads will enter the critical section simultaneously, violating mutual exclusion
BThread B will be permanently blocked waiting for Thread A to release the lock
CThe lock variable will overflow from concurrent writes
DThis is not a problem — only one thread can read memory at a time
If read and write are separate operations, both threads can observe the lock as free before either marks it as taken. Both then mark it as taken and proceed into the critical section together — mutual exclusion is violated. This is precisely why hardware provides atomic instructions like test-and-set and compare-and-swap: they make the check and the claim a single indivisible step so no thread can be interrupted between them.
Question 2 Multiple Choice
A system uses a blocking lock for a critical section that consistently completes in 3 nanoseconds on a modern CPU. A performance engineer proposes switching to a spinlock. What is the likely performance outcome?
AWorse — spinlocks waste CPU cycles that could serve other threads during longer critical sections
BBetter — for a very short critical section, avoiding two context switches (sleep + wake) outweighs the cost of briefly spinning
CIdentical — spinlocks and blocking locks have the same overhead by design
DWorse — spinlocks cannot guarantee mutual exclusion for nanosecond-scale operations
The spinlock vs. blocking lock tradeoff hinges on how long the wait is. A context switch takes thousands to tens of thousands of nanoseconds. For a 3 ns critical section, the cost of putting a thread to sleep and waking it back up vastly exceeds the cost of spinning briefly. Spinlocks are efficient for very short critical sections with low contention; blocking locks are better when critical sections are long enough that the CPU can do useful work during the wait.
Question 3 True / False
A correctly implemented spinlock guarantees that nearly every thread waiting for the lock will eventually acquire it.
TTrue
FFalse
Answer: False
A naive spinlock does not guarantee starvation freedom. On some hardware, memory access patterns can favor one core over others, causing a thread to spin indefinitely while another repeatedly re-acquires the lock. Ticket locks address this by assigning each waiting thread a number and serving them in order, guaranteeing that every thread eventually gets its turn. Starvation freedom is a design property that must be explicitly built in — it does not come for free from mutual exclusion alone.
Question 4 True / False
The fundamental hardware requirement for any correct lock implementation is an operation that atomically reads and writes a shared variable in a single indivisible step.
TTrue
FFalse
Answer: True
Without atomicity, any implementation of acquire() that checks 'is the lock free?' and then 'marks it as taken' is vulnerable to interleaving: two threads can both pass the check before either marks it. Hardware atomic instructions — test-and-set, compare-and-swap, fetch-and-add — make the read-modify-write sequence indivisible. This hardware primitive is the foundation that all higher-level synchronization mechanisms are built on.
Question 5 Short Answer
Why must the 'check if free' and 'mark as taken' steps of a lock's acquire() operation be atomic, and what goes wrong if they are not?
Think about your answer, then reveal below.
Model answer: If the two steps are separate, a thread can be interrupted or preempted between them. Thread A reads the lock as free; before it marks it taken, Thread B also reads the lock as free; both then mark it taken and both enter the critical section simultaneously — violating mutual exclusion and potentially corrupting shared state. Atomic instructions prevent this by making the check-and-claim a single step that cannot be interrupted.
This is the core correctness requirement for locking. Any implementation that separates the read from the write — even by a single instruction — opens a race condition window. The entire value of a lock is that exactly one thread can be in the critical section at a time; non-atomic acquire() destroys this guarantee.