Skip to content

Deadlocks

4.1 Definition and Necessary Conditions

A deadlock is a situation where a set of processes are all blocked, each waiting for a resource Held by another process in the set.

Coffman conditions (all four must hold simultaneously):

  1. Mutual exclusion: At least one resource is non-sharable.
  2. Hold and wait: A process holds at least one resource and is waiting for others.
  3. No preemption: Resources cannot be forcibly taken.
  4. Circular wait: A circular chain of processes exists, each waiting for a resource held by the next.

4.2 Deadlock Prevention

Eliminate one of the four Coffman conditions:

ConditionPrevention strategy
Mutual exclusionGenerally not feasible for non-sharable resources
Hold and waitRequire all resources upfront before execution
No preemptionRelease held resources if a new request cannot be granted
Circular waitImpose a total ordering on resources; request in increasing order

4.3 Deadlock Avoidance: Banker”s Algorithm

The Banker’s algorithm avoids deadlock by checking whether granting a request leads to a safe State.

Definitions:

  • Available: Vector A=(A1,,Am)A = (A_1, \ldots, A_m) of available instances of each resource type.
  • Maximum: Matrix Max\mathrm{Max} where Max[i][j]\mathrm{Max}[i][j] is the maximum demand of process ii for resource jj.
  • Allocation: Matrix Alloc\mathrm{Alloc} where Alloc[i][j]\mathrm{Alloc}[i][j] is the current allocation.
  • Need: Need[i][j]=Max[i][j]Alloc[i][j]\mathrm{Need}[i][j] = \mathrm{Max}[i][j] - \mathrm{Alloc}[i][j].

Safety algorithm:

1. Work = Available; Finish[i] = false for all i
2. Find i such that Finish[i] == false AND Need[i] <= Work
3. If no such i exists, go to step 5
4. Work = Work + Alloc[i]; Finish[i] = true; go to step 2
5. If Finish[i] == true for all i, system is SAFE

Resource request algorithm. When process ii requests resources:

1. If Request[i] > Need[i], error (exceeded maximum claim)
2. If Request[i] > Available, process must wait
3. Pretend to allocate:
Available = Available - Request[i]
Alloc[i] = Alloc[i] + Request[i]
Need[i] = Need[i] - Request[i]
4. Run safety algorithm.
If safe, grant the request.
If unsafe, roll back and make the process wait.

Example. Five processes, three resource types, A=(3,3,2)A = (3, 3, 2):

ProcessAllocationMaxNeed
P0P_0(0,1,0)(7,5,3)(7,4,3)
P1P_1(2,0,0)(3,2,2)(1,2,2)
P2P_2(3,0,2)(9,0,2)(6,0,0)
P3P_3(2,1,1)(2,2,2)(0,1,1)
P4P_4(0,0,2)(4,3,3)(4,3,1)

Safety check: P1P_1 has Need=(1,2,2)(3,3,2)=A\mathrm{Need} = (1,2,2) \leq (3,3,2) = A. Execute P1P_1Release (2,0,0)(2,0,0)New A=(5,3,2)A = (5,3,2). Then P3P_3: Need=(0,1,1)(5,3,2)\mathrm{Need} = (0,1,1) \leq (5,3,2). Continuing, All processes can complete: system is safe.

Worked Example 4.1 — Banker's Algorithm Step-by-Step

Given the state above, suppose P1P_1 requests (1,0,2)(1,0,2).

Step 1: Verify Request1=(1,0,2)Need1=(1,2,2)\mathrm{Request_1} = (1,0,2) \leq \mathrm{Need_1} = (1,2,2). OK.

Step 2: Verify Request1=(1,0,2)A=(3,3,2)\mathrm{Request_1} = (1,0,2) \leq A = (3,3,2). OK.

Step 3: Pretend to allocate:

ProcessAllocationMaxNeed
P0P_0(0,1,0)(7,5,3)(7,4,3)
P1P_1(3,0,2)(3,2,2)(0,2,0)
P2P_2(3,0,2)(9,0,2)(6,0,0)
P3P_3(2,1,1)(2,2,2)(0,1,1)
P4P_4(0,0,2)(4,3,3)(4,3,1)

A=(3,3,2)(1,0,2)=(2,3,0)A = (3,3,2) - (1,0,2) = (2,3,0).

Step 4: Run safety algorithm.

  1. P1P_1: Need=(0,2,0)(2,3,0)\mathrm{Need} = (0,2,0) \leq (2,3,0). Execute, A=(2,3,0)+(3,0,2)=(5,3,2)A = (2,3,0) + (3,0,2) = (5,3,2).
  2. P3P_3: Need=(0,1,1)(5,3,2)\mathrm{Need} = (0,1,1) \leq (5,3,2). Execute, A=(5,3,2)+(2,1,1)=(7,4,3)A = (5,3,2) + (2,1,1) = (7,4,3).
  3. P4P_4: Need=(4,3,1)(7,4,3)\mathrm{Need} = (4,3,1) \leq (7,4,3). Execute, A=(7,4,3)+(0,0,2)=(7,4,5)A = (7,4,3) + (0,0,2) = (7,4,5).
  4. P0P_0: Need=(7,4,3)(7,4,5)\mathrm{Need} = (7,4,3) \leq (7,4,5). Execute, A=(7,4,5)+(0,1,0)=(7,5,5)A = (7,4,5) + (0,1,0) = (7,5,5).
  5. P2P_2: Need=(6,0,0)(7,5,5)\mathrm{Need} = (6,0,0) \leq (7,5,5). Execute, A=(7,5,5)+(3,0,2)=(10,5,7)A = (7,5,5) + (3,0,2) = (10,5,7).

All processes finish. The request is granted. Safe sequence: P1,P3,P4,P0,P2\langle P_1, P_3, P_4, P_0, P_2 \rangle.

Worked Example 4.2 — Unsafe State Detection

Suppose instead P0P_0 requests (0,2,0)(0,2,0) in the original state.

Step 1: Request0=(0,2,0)Need0=(7,4,3)\mathrm{Request_0} = (0,2,0) \leq \mathrm{Need_0} = (7,4,3). OK.

Step 2: Request0=(0,2,0)A=(3,3,2)\mathrm{Request_0} = (0,2,0) \leq A = (3,3,2). OK.

Step 3: Pretend to allocate. New A=(3,1,2)A = (3,1,2), Need0=(7,2,3)\mathrm{Need_0} = (7,2,3).

Step 4: Safety check. No process can execute: P1P_1 needs (1,2,2)(1,2,2) but only (3,1,2)(3,1,2) available (second component insufficient). P3P_3 needs (0,1,1)(3,1,2)(0,1,1) \leq (3,1,2) — OK, execute P3P_3: A=(5,2,3)A = (5,2,3). Then P1P_1: (1,2,2)(5,2,3)(1,2,2) \leq (5,2,3) — OK, execute: A=(7,2,3)A = (7,2,3). But now P0P_0 needs (7,2,3)(7,2,3) — exact match, execute: A=(7,3,3)A = (7,3,3). P4P_4: (4,3,1)(7,3,3)(4,3,1) \leq (7,3,3) — OK, execute: A=(7,3,5)A = (7,3,5). P2P_2: (6,0,0)(7,3,5)(6,0,0) \leq (7,3,5) — OK.

Safe sequence: P3,P1,P0,P4,P2\langle P_3, P_1, P_0, P_4, P_2 \rangle. The request is granted.

Theorem 4.1. The Banker’s algorithm is correct: if it declares a state safe, there exists a Sequence of process completions that avoids deadlock.

Proof. The safety algorithm constructs an explicit sequence. Each process in the sequence can run To completion with the currently available resources plus those released by previously completed Processes. If no such sequence exists, there is a set of processes whose combined needs exceed Available resources. \blacksquare

4.4 Deadlock Detection

Allow deadlocks to occur, then detect and recover.

Detection algorithm:

1. Work = Available
Finish[i] = false if Alloc[i] != 0; true otherwise
2. Find i such that Finish[i] == false AND Request[i] <= Work
3. If found: Work = Work + Alloc[i]; Finish[i] = true; go to step 2
4. If not found: processes with Finish[i] == false are deadlocked

Recovery strategies:

  1. Process termination: Kill deadlocked processes one at a time until the cycle is broken.
  2. Resource preemption: Forcibly reclaim resources; must handle rollback of the affected process.
  3. Checkpoint and rollback: Periodically save process state; restore on deadlock.
Worked Example 4.3 — Deadlock Detection

Three processes and one resource type with 10 instances:

ProcessAllocationMaximum
P0P_059
P1P_124
P2P_227

Available = 10(5+2+2)=110 - (5 + 2 + 2) = 1.

Request0=4\mathrm{Request_0} = 4, Request1=2\mathrm{Request_1} = 2, Request2=5\mathrm{Request_2} = 5.

Detection:

  1. Work = 1. No process has Request1\mathrm{Request} \leq 1.
  2. No process can proceed. All three are deadlocked.

The system is in an unsafe state with a deadlock involving {P0,P1,P2}\{P_0, P_1, P_2\}.

Recovery: Preempt 3 units from P0P_0 (reducing its allocation to 2). Now Available = 4. P1P_1 can proceed (Request1=24\mathrm{Request_1} = 2 \leq 4). After P1P_1 finishes, Available = 4+2=64 + 2 = 6. P0P_0: Request0=46\mathrm{Request_0} = 4 \leq 6Proceeds. After: Available = 6+5=116 + 5 = 11. P2P_2 proceeds. Deadlock resolved.

4.5 Deadlock Handling in Practice

Most modern OSes (Linux, Windows) use the ostrich algorithm: ignore deadlocks and rely on process Termination or reboot. Rationale: deadlocks are rare, detection is expensive, and prevention is Overly restrictive.