How waiting becomes circular

Suppose thread A holds one lock and thread B holds another. Each then requests the lock owned by the other, and neither can finish and release its first lock without obtaining the second. The resulting dependency prevents either from progressing. Both locks may correctly enforce mutual exclusion while the combined program fails to advance.

Database transactions can produce the same pattern by locking different records and subsequently requesting each other's records. Deadlock is not simply an unusually long wait: the action needed to end a wait is itself dependent on a waiting participant. Code capable of deadlocking need not deadlock on every run; the actual interleaving determines whether the state is reached.

References: [2] [4] [1]

Four conditions in the resource model

Classical resource-deadlock analysis identifies four necessary conditions: resources require exclusive use; participants hold resources while waiting for more; allocated resources cannot be forcibly reclaimed; and the waiting dependencies form a cycle. Preventing any one condition rules out deadlock within this model.

These conditions describe a structure, not a reason to classify every blocked operation as deadlocked. A mutex alone is insufficient evidence. Analysis must identify the relevant resources, available instances, and actual dependencies. A diagram or a report that a thread is waiting is meaningful only with those assumptions made explicit.

References: [1]

Prevention, avoidance, and recovery

Prevention constrains resource use so that a necessary condition cannot arise. A consistent acquisition order for multiple locks removes circular waiting. Avoidance instead uses information about possible future requests to select safe scheduling or allocation decisions. Such information may be difficult to obtain, and conservative choices can reduce concurrency.

Detection and recovery permit a deadlock to arise and then intervene. PostgreSQL automatically detects deadlocks and aborts one involved transaction so that others can proceed. Applications should not assume which transaction will be selected. Aborted work may need to be retried; recovery does not turn its unfinished operations into successful ones. Consistent lock ordering remains useful even when a detector exists.

References: [1] [4]

Livelock and starvation are different

In livelock, participants keep taking actions but their responses to one another prevent useful progress. For example, contenders may repeatedly give up acquired locks and retry in a pattern that recreates interference. Unlike a deadlocked group, they are not simply blocked. Replacing a blocking request with a busy retry loop therefore does not establish that work will finish.

Starvation concerns a participant that persistently fails to obtain necessary resources or opportunities while others may continue completing work. These are all progress failures, but they call for different diagnoses. Processor activity does not prove that an algorithm advances, and successful requests elsewhere do not prove that every waiting participant will eventually succeed.

References: [3] [1]