Operating Systems Cheatsheet

Deadlocks

Use this Operating Systems reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Deadlock Definition

A set of processes is in deadlock if every process in the set is waiting for an event that can only be caused by another process in the set — creating a circular wait with no possibility of progress.

The Four Necessary Conditions (Coffman Conditions)

All four must hold simultaneously for deadlock to occur.

ConditionMeaningExample
Mutual exclusionResources are non-shareableOnly one process can hold a printer
Hold and waitProcess holds ≥1 resource while waiting for moreHolds lock A, waits for lock B
No preemptionResources cannot be forcibly taken awayOS can't revoke a held mutex
Circular waitChain P₁ → r₁ → P₂ → r₂ → … → Pₙ → rₙ → P₁P1 waits for P2's lock; P2 waits for P1's lock

Preventing any one condition prevents deadlock.

Resource Allocation Graph (RAG)

Directed graph with two node types: - Process nodes (circles): P₁, P₂, … - Resource nodes (boxes, dots = instances): R₁, R₂, …

EdgeDirectionMeaning
Request edgePᵢ → RⱼProcess i is waiting for resource j
Assignment edgeRⱼ → PᵢOne instance of resource j is held by process i

Deadlock detection rule: - If all resources have 1 instance: cycle in RAG ⟺ deadlock - If resources have multiple instances: cycle is necessary but not sufficient — use banker's algorithm to confirm

Deadlock Handling Strategies

StrategyApproachOverheadUsed in
PreventionEliminate ≥1 Coffman conditionDesign-time constraintsSome embedded/RT systems
AvoidanceAllocate only if safe state remainsPer-request checkRarely (overhead)
Detection + RecoveryAllow deadlock; detect and break itDetection costMany OSes
Ignore (ostrich)Assume it never happens / very rareNoneLinux, Windows

Deadlock Prevention

Attack each Coffman condition:

ConditionPrevention methodDrawback
Mutual exclusionMake resources sharable (e.g., read-only files)Not always possible
Hold and waitRequest all resources at once before startLow utilization; starvation
No preemptionPreempt resources from waiting processOnly for state-saveable resources
Circular waitImpose total ordering on resource types; always acquire in orderOrdering can be cumbersome

Deadlock Avoidance

OS tracks resource allocation state and only grants requests that keep system in a safe state.

Safe state: there exists a sequence ⟨P₁, P₂, …, Pₙ⟩ such that each Pᵢ's remaining resource needs can be satisfied by currently available resources + those held by P₁ … Pᵢ₋₁.

safe ⟹ no deadlock (but unsafe ≠ deadlock)

Banker's Algorithm

For each resource type with multiple instances.

Data structures (n processes, m resource types):

ArraySizeMeaning
Available[j]mFree instances of resource j
Max[i][j]n×mMax demand of process i for resource j
Allocation[i][j]n×mCurrent allocation of j to i
Need[i][j]n×mMax[i][j] − Allocation[i][j]

Safety algorithm:

Work = Available
Finish[i] = false for all i

repeat:
    find i such that Finish[i]=false AND Need[i] ≤ Work
    if found:
        Work += Allocation[i]
        Finish[i] = true
until no such i

if all Finish[i] = true → SAFE
else → UNSAFE

Resource-request algorithm:

Process i requests Request[i]:
1. If Request[i] > Need[i] → error
2. If Request[i] > Available → wait (not enough)
3. Tentatively allocate:
   Available -= Request[i]
   Allocation[i] += Request[i]
   Need[i] -= Request[i]
4. Run safety algorithm
5. If safe → grant; else → rollback, wait

Banker's Example

3 processes (P0, P1, P2), 3 resources (A=10, B=5, C=7 total)

ProcessAllocation A B CMax A B CNeed A B C
P00 1 07 5 37 4 3
P12 0 03 2 21 2 2
P23 0 29 0 26 0 0

Available = (3, 3, 2). Safe sequence: P1 → P2 → P0

Deadlock Detection

Single Instance: Cycle Detection in RAG

Use DFS; if back-edge found → cycle → deadlock.

Multiple Instances: Detection Algorithm

Same as banker's safety check but using Allocation (not Need):

Work = Available
Finish[i] = (Allocation[i] == 0) for all i

repeat:
    find i: Finish[i]=false AND Request[i] ≤ Work
    if found:
        Work += Allocation[i]; Finish[i] = true
until no such i

if some Finish[i] = false → those processes are deadlocked

When to run: periodically or whenever a resource request cannot be granted immediately. Frequent runs → fast detection but high CPU cost.

Deadlock Recovery

MethodDescriptionCost
Abort allKill all deadlocked processesHigh — all work lost
Abort one at a timeKill cheapest; re-run detectionIterative overhead
Resource preemptionTake resource from victim, rollbackNeeds checkpointing; starvation risk

Victim selection criteria: priority, elapsed time, resources held, resources still needed, how many times already rolled back.

Lock Ordering (Prevention in Practice)

// Always acquire in a consistent global order to break circular wait:
// Rule: always lock mu1 before mu2

void transfer(Account *from, Account *to, int amount) {
    pthread_mutex_t *first  = (from->id < to->id) ? &from->lock : &to->lock;
    pthread_mutex_t *second = (from->id < to->id) ? &to->lock : &from->lock;
    pthread_mutex_lock(first);
    pthread_mutex_lock(second);
    from->balance -= amount;
    to->balance   += amount;
    pthread_mutex_unlock(second);
    pthread_mutex_unlock(first);
}

Livelock vs Starvation

ConditionDescriptionExample
DeadlockProcesses blocked, no progressEach holds one lock, waits for the other
LivelockProcesses active but making no progressTwo politely step aside for each other forever
StarvationProcess indefinitely delayedLow-priority process never scheduled