skip to content

How can a writer be starved when a lock has shared and exclusive modes, and what admission policies exist to prevent it?

level: middleimportance: must knowfreq 54%

answer

  1. writer needs reader count == 0
  2. reader preference: count never drains
  3. writer preference blocks arriving readers
  4. no recursive read acquire under writer preference
  5. fair FIFO batches readers; phase-fair alternates

basics

~20 s

Under reader preference, new readers join while readers are already inside, so if reads arrive faster than they finish the reader count never reaches zero and a waiting writer never runs. Writer-preference blocks arriving readers once a writer is queued; fair or phase-fair policies queue both by arrival and alternate between them.

solid answer

~50 s

Exclusive mode can only be granted when the reader count is zero. Under a **reader-preference** policy, a reader that arrives while other readers hold the lock is admitted immediately, even if a writer is queued. If the read arrival rate is high enough that reads overlap, the count never drains, and the writer waits indefinitely — starvation, not deadlock, because the system is still making progress for readers. **Writer-preference** fixes it: once a writer is queued, arriving readers are blocked behind it, the existing readers drain, and the writer gets in. The mirror risk is a stream of writers starving readers, and a subtler hazard: a thread that already holds shared mode and tries to acquire shared mode again now deadlocks against the queued writer, so read-mode reentrancy stops being safe. **Fair / FIFO** policies queue all requests by arrival, batching consecutive readers. **Phase-fair** designs alternate reader and writer phases, bounding both waits. Know which policy your lock uses before trusting it under load.

code

text · 8 lines
text
time ---------------------------------------------->
R1  [==read==]
R2       [====read====]
R3            [===read===]
R4                 [=====read=====]
W        wants exclusive .... waiting .... still waiting

reader count: 1  2  2  3  2  3  2 ...   never 0 -> W never admitted

go deeper

for a junior

Explain that a writer needs zero readers and that constantly arriving readers can keep that from ever happening.

for a middle

Name the three policies — reader-preference, writer-preference, fair — and describe the arrival pattern that starves a writer.

for a senior

Add the writer-preference hazards: reader latency spikes and unsafe recursive read acquisition; describe detecting starvation via the exclusive-acquire tail.

for a principal

Frame it as an admission-control policy with an SLA attached — decide whether stale data or blocked reads is the worse failure, and consider removing the contest entirely by building state off-lock and swapping it in.

## Why starvation is possible at all A writer needs the lock empty. Readers, by design, come and go independently and can overlap. That means the reader count is a value that only reaches zero if the arrival pattern lets it. The question every readers-writer lock must answer is the **admission rule**: when a writer is waiting and a new reader arrives while other readers are inside, do we let the newcomer in? The answer defines the policy. ## Reader preference Admit the reader whenever the lock is not exclusively held. Advantages: maximum read throughput, simplest fast path, no reader ever waits for another reader. Failure mode: with a sustained read rate high enough that read intervals overlap, the count never hits zero and the queued writer waits forever. This is **starvation** — a liveness failure, not a safety one. Nothing is corrupted and readers are perfectly happy, which is exactly why it is hard to notice. It typically appears in production as stale data (the refresh task never gets to run) or as a slowly growing write queue, rather than as an obvious hang. A classic real symptom: a config cache is refreshed by a background writer, request threads read constantly, and under peak traffic the refresh silently stops happening; the data goes stale for the whole peak and updates itself once load drops. ## Writer preference Once a writer is queued, block newly arriving readers behind it. Existing readers drain, the writer runs, then readers resume. This bounds writer latency, at three costs: 1. **Reader latency spikes** — every arriving reader waits out the write section. 2. **Writer floods can starve readers** — the mirror image, if writers keep arriving. 3. **Read-mode re-entry becomes deadlock-prone.** Suppose a thread holds shared mode and, deeper in its call stack, acquires shared mode again. Under reader preference that succeeded. Under writer preference, if a writer queued in between, the second shared acquire blocks behind that writer, while the writer blocks behind the thread's own first shared hold. The thread deadlocks against itself through the writer. This is why recursive read acquisition is documented as unsafe for many writer-preferring locks, and why nested read sections are a smell. ## Fair (FIFO) and phase-fair policies **Fair / FIFO**: put every request, shared or exclusive, into one arrival-ordered queue. Consecutive readers at the head are granted as a batch (they are mutually compatible), so you keep some read parallelism while guaranteeing bounded waiting for everyone. Cost: a reader arriving behind a queued writer waits even though the lock is currently held only in shared mode, so peak read throughput drops. **Phase-fair**: alternate phases explicitly — a reader phase admits all readers waiting at the phase start, then a writer phase admits one writer, and so on. This bounds both reader and writer waits regardless of arrival rates, which is why it is favoured in real-time systems where a worst-case bound must exist. ## How to choose, and how to detect the problem - If writes carry freshness requirements (config refresh, invalidation, index updates), do not use reader preference. A stalled writer means stale data for everyone. - If writes are rare and non-urgent and read throughput is the objective, reader preference is acceptable — but instrument it. - If you need predictability, choose fair or phase-fair and accept lower peak read throughput. - Some implementations offer a middle ground equivalent to bounded barging: allow reader overtaking until a writer has waited N grants or T milliseconds, then switch to writer preference. This preserves most throughput while capping writer latency. Detection is straightforward once you look: record time-to-acquire for exclusive mode as a histogram, and alert on the maximum, not the mean. A starving writer shows up as an unbounded tail on that one metric while everything else looks healthy. Also watch for a rising 'time since last successful write' on any refresh path. One more design escape: if the writer is only refreshing data, you can avoid the contest entirely by having the writer build a new copy off-lock and swap it in under a very short exclusive section — the writer then needs the lock for nanoseconds, so even a reader-preferring policy will let it in.

  • Why can acquiring shared mode twice on the same thread deadlock under a writer-preferring policy?
    The first shared hold keeps the reader count above zero, so a writer that queues afterwards cannot proceed. The nested second shared acquire is then blocked behind that queued writer, since writer preference stops admitting new readers. The thread is now waiting for a writer that is waiting for the thread's own first hold to be released, which is a cycle it can never break.
  • A background refresh writer appears to have stopped running under peak traffic, but nothing is deadlocked. What do you check?
    This is the signature of writer starvation under a reader-preferring lock: overlapping readers keep the reader count above zero so the writer is never admitted. Check the exclusive-acquire wait-time maximum and the time since the last successful refresh. Fix it by switching to a writer-preferring or fair policy, or by having the writer build the new state off-lock and swap it in under a critical section short enough to slip through.

saying these in an interview costs you the question

  • Calls writer starvation a deadlock
  • Assumes every readers-writer lock is fair by default
  • Thinks writer preference has no downside
  • Acquires shared mode recursively without checking the policy
  • Monitors mean acquisition time instead of the tail, so starvation stays invisible

context