G27
Self-checkedTier 2 17 min reados.deadlock-detection-recovery

Deadlock detection and recovery

Letting deadlocks happen and then dealing with them: the wait-for graph for single-instance resources, the detection algorithm for several instances (Allocation, Request, Work), how it differs from the Banker's safety test, how often to run it, and recovery by terminating processes or preempting resources, including how to choose the cheapest victims.

Why it exists

Prevention and avoidance cost something: prevention restricts how processes may ask for resources, and the Banker's algorithm needs every process to declare its maximum need in advance and is slow. Many systems choose a third way: let deadlocks happen, notice them, and recover. That needs two things: an algorithm that detects a deadlock (and says which processes are in it) and a policy that recovers (which processes to abort, or which resources to take away). GATE asks for the cycle in a wait-for graph, for the result of the detection algorithm on matrices, for which processes are deadlocked, and for the difference from the Banker's safety test.

Prerequisites

You need the four conditions of a deadlock, the resource allocation graph, and the safety algorithm of the Banker's page, which the detection algorithm closely resembles.

Precise definitions

The four ways to treat deadlocks. Prevention (make one of the four conditions impossible), avoidance (grant only requests that keep the state safe), detection and recovery (allow deadlock, find it, break it), and ignoring it (leave it to the user; this is what UNIX and Windows do for most resources, because deadlocks are rare and the other methods are expensive).

Single instance of each resource: the wait-for graph. Take the resource allocation graph and remove the resource nodes: draw an edge Pi→PjP_i \to P_j if PiP_i is waiting for a resource that PjP_j holds. With one instance per resource,

deadlock  ⟺  the wait-for graph has a cycle.\text{deadlock} \iff \text{the wait-for graph has a cycle}.

Finding a cycle by a depth-first search takes time proportional to the number of edges, at most n2n^2 for nn processes; the system must also keep the graph up to date as processes ask and release.

Several instances: the detection algorithm. The data structures have the same shape as the Banker's, but Max is replaced by what the processes are asking for now:

  • Available (a vector of mm numbers), Allocation (n×mn \times m), and Request (n×mn \times m): Request[i][j] is the number of instances of resource jj that process ii is currently waiting for.

The algorithm:

  1. Work = Available. For every process, Finish[i] = false if it holds anything (Allocation[i] is not all zero), and true if it holds nothing (such a process cannot be holding anybody up).
  2. Find an i with Finish[i] = false and Request[i] <= Work (componentwise). If there is none, go to step 4.
  3. Assume process ii gets what it asks for, runs to the end and returns what it holds: Work = Work + Allocation[i], Finish[i] = true. Go to step 2.
  4. If every Finish[i] is true, there is no deadlock. Otherwise every process with Finish[i] = false is deadlocked.

It takes of the order of m⋅n2m \cdot n^2 operations (at most nn passes, each testing at most nn processes against mm numbers). The algorithm is optimistic: it assumes that a process that is not waiting will not ask for more. If it does, the next run of the algorithm will see it. The set of deadlocked processes does not depend on which eligible process is picked first, but the order in which processes can finish usually does.

Detection against the Banker's safety test. They are the same loop, with a different matrix compared with Work:

Banker's safety testDetection
compares Work withNeed = Max - Allocation (worst case still to come)Request (what is wanted now)
a process holding nothingstill counted (it needs its Need)starts as finished
a failure meansthe state is unsafe: deadlock is possiblethe processes left are deadlocked now
used foravoidance, before a request is granteddetection, after the fact

A deadlocked state is always unsafe; an unsafe state need not be deadlocked.

Processes that wait behind a deadlock. A process that is not on a cycle but waits for a resource held by a deadlocked process can never finish either. The algorithm leaves it unfinished and lists it with the deadlocked processes.

When to run detection. After every request that cannot be granted at once (finds the deadlock at the moment it forms and names the process whose request closed the cycle, but costs a lot); at fixed intervals; or when something suggests trouble, for example when CPU utilisation falls below a threshold. The more rarely it runs, the more processes are stuck by the time it finds them, and with many cycles in the graph it is hard to tell which process "caused" the deadlock.

Recovery 1: terminate processes.

  • Abort all deadlocked processes. Certain to break the deadlock, expensive: all their computation is lost.
  • Abort one at a time until the deadlock is gone, running the detection algorithm after each abort: less is lost, but more detection work.

Choosing the victim: the priority of the process; how long it has run and how long it still needs; the resources it holds and how many more it needs; how many processes would have to be terminated; whether it is interactive or batch; whether it is in the middle of an update that leaves a file inconsistent.

Recovery 2: preempt resources. Take resources away from one process and give them to another until the cycle is broken. Three issues: selecting a victim (minimise cost, as above); rollback (the victim cannot continue as if nothing happened: roll it back to a safe state or to its start and restart it); and starvation (the same process may be picked every time: include the number of rollbacks in the cost so that it is not).

How it works

A resource allocation graph with five processes and five resources of one instance each. Step through the requests: two cycles form, one through P1, P2 and P3 and one between P4 and P5, and P2 is also waiting for a resource held by P4. Then two victims are aborted, one from each cycle.

Two deadlocks, two victims: start

Graph analysed by script, not typed
no cycle in the graph0no deadlock
P1P2P3P4P5R1R2R3R4R5

Dashed arrow: a request (process to resource). Solid arrow: an assignment (resource to the process that holds an instance). Filled dot: an instance in use.

Step 0

Five processes, five resources with ONE instance each. Step through the requests: assignment edges point from a resource to its holder, request edges from a waiting process to the resource.

0 / 13
computed by script: the deadlock detection algorithm, graph analysis and a search over sets of victims. python3 scripts/gen-os-deadlock2-traces.py

The wait-for graph of the final state: for each waited-for resource, the process that holds it.

From the resource allocation graph to the wait-for graph: start

Computed by a script, not typed
processholdswaits for resourcewaits for process
P1
P2
P3
P4
P5

Step 0

With one instance per resource, replace 'P waits for R, held by Q' by the single edge P → Q. A cycle of the wait-for graph is a deadlock. Edges: P1 → P2, P2 → P3, P2 → P4, P3 → P1, P4 → P5, P5 → P4. Nodes on cycles: P1, P2, P3, P4, P5.

0 / 5
computed by script: the deadlock detection algorithm, graph analysis and a search over sets of victims. python3 scripts/gen-os-deadlock2-traces.py

Which processes to abort? Every process alone fails (each cycle needs a victim); every smallest sufficient set has one process from each cycle; with the costs shown, aborting P3 and P5 is the cheapest. The table was filled by aborting the processes in the graph and running the deadlock test again.

Which processes to abort? Every single process and every smallest sufficient pair: start

Computed by a script, not typed
victimsdeadlock gone?total cost
abort P1
abort P2
abort P3
abort P4
abort P5
abort P1 and P4
abort P1 and P5
abort P2 and P4
abort P2 and P5
abort P3 and P4
abort P3 and P5

Step 0

Costs (work lost if the process is aborted): P1 3, P2 5, P3 2, P4 4, P5 1. A smallest sufficient set contains one process from each cycle: {P1, P2 or P3} and {P4 or P5}. Tried by aborting the processes in the graph and re-running the deadlock test.

0 / 11
computed by script: the deadlock detection algorithm, graph analysis and a search over sets of victims. python3 scripts/gen-os-deadlock2-traces.py

The detection algorithm on matrices: five processes, three resource types with totals (9,4,6)(9, 4, 6). First a state without a deadlock. The scan starts at P0, which asks for more than is free, so P1 goes first.

Deadlock detection with several instances: no deadlock: start

Algorithm run by script, not typed
processalloc Aalloc Balloc Crequest Arequest Brequest Cfinish
P0110211-
P1302010-
P2211000-
P3102101-
P4110201-
total (A B C)
9 4 6
available (A B C)
1 1 1
work (A B C)
-
finish order so far-

Step 0

Resources A, B, C have totals (9, 4, 6). Allocation is what each process holds now. Request is what it is WAITING for right now (not its maximum claim: the detection algorithm does not know the maximum). Available = Total - total allocation = (1, 1, 1). We ask: is any set of processes deadlocked?

0 / 7
computed by script: the deadlock detection algorithm, graph analysis and a search over sets of victims. python3 scripts/gen-os-deadlock2-traces.py

Then a state in which P0 and P4 each hold one B and each wait for three more:

Deadlock detection with several instances: P0 and P4 are deadlocked: start

Algorithm run by script, not typed
processalloc Aalloc Balloc Crequest Arequest Brequest Cfinish
P0110030-
P1302010-
P2211000-
P3102101-
P4110030-
total (A B C)
9 4 6
available (A B C)
1 1 1
work (A B C)
-
finish order so far-

Step 0

Resources A, B, C have totals (9, 4, 6). Allocation is what each process holds now. Request is what it is WAITING for right now (not its maximum claim: the detection algorithm does not know the maximum). Available = Total - total allocation = (1, 1, 1). We ask: is any set of processes deadlocked?

0 / 5
computed by script: the deadlock detection algorithm, graph analysis and a search over sets of victims. python3 scripts/gen-os-deadlock2-traces.py

The same allocation seen by the Banker's safety test and by detection:

Detection and the Banker's safety test on the same allocation: start

Computed by a script, not typed
state (same Allocation and Available, three different claims)Banker's safety test (uses Max - Allocation)Detection (uses the present Request)
A: nobody is waiting, but each may still claim 2 more of everything
B: Max = Allocation + Request of the first example (the maximum claim equals the present request)
C: Max = Allocation + Request of the deadlocked example

Step 0

The two algorithms are the same loop; they differ in the matrix compared with Work (worst-case Need or present Request) and in what the answer means: 'unsafe' means deadlock is possible, 'deadlock' means it exists now.

0 / 3
computed by script: the deadlock detection algorithm, graph analysis and a search over sets of victims. python3 scripts/gen-os-deadlock2-traces.py

Worked examples

EX 1From the graph to the wait-for graph

Easy

In the graph of the animation, P1 holds R1 and waits for R2; P2 holds R2 and waits for R3 and R4; P3 holds R3 and waits for R1; P4 holds R4 and waits for R5; P5 holds R5 and waits for R4. Since every resource has one instance, "P1 waits for R2, held by P2" becomes the single edge P1 → P2, and so on: P1 → P2, P2 → P3, P2 → P4, P3 → P1, P4 → P5, P5 → P4. The cycles are P1 → P2 → P3 → P1 and P4 → P5 → P4: both are deadlocks. P2 is on the first cycle and also waits for P4, but it is not on the second cycle. Aborting P2 breaks the first cycle only; it does not help with P4 and P5.

EX 2The detection algorithm in C

Medium

The two matrix examples of the animations: Allocation, Available =(1,1,1)= (1, 1, 1) and the two Request matrices.

#include <stdio.h>
 
#define N 5
#define M 3
 
static const int alloc[N][M] = {{1, 1, 0}, {3, 0, 2}, {2, 1, 1}, {1, 0, 2}, {1, 1, 0}};
static const int avail[M] = {1, 1, 1};
 
static void detect(const char *title, const int req[N][M]) {
    int work[M], finish[N], order[N], k = 0;
    for (int j = 0; j < M; j++) work[j] = avail[j];
    for (int i = 0; i < N; i++) finish[i] = (alloc[i][0] == 0 && alloc[i][1] == 0 && alloc[i][2] == 0);
    int moved = 1;
    while (moved) {
        moved = 0;
        for (int i = 0; i < N && !moved; i++) {
            if (finish[i]) continue;
            int fits = 1;
            for (int j = 0; j < M; j++)
                if (req[i][j] > work[j]) fits = 0;
            if (fits) {
                for (int j = 0; j < M; j++) work[j] += alloc[i][j];
                finish[i] = 1;
                order[k++] = i;
                moved = 1;
            }
        }
    }
    printf("%s\n  finish order:", title);
    for (int i = 0; i < k; i++) printf(" P%d", order[i]);
    printf("\n  deadlocked:");
    int any = 0;
    for (int i = 0; i < N; i++)
        if (!finish[i]) { printf(" P%d", i); any = 1; }
    printf("%s\n  final Work = (%d, %d, %d)\n", any ? "" : " none", work[0], work[1], work[2]);
}
 
int main(void) {
    const int ok[N][M] = {{2, 1, 1}, {0, 1, 0}, {0, 0, 0}, {1, 0, 1}, {2, 0, 1}};
    const int dl[N][M] = {{0, 3, 0}, {0, 1, 0}, {0, 0, 0}, {1, 0, 1}, {0, 3, 0}};
    detect("first example", ok);
    detect("second example", dl);
    return 0;
}
first example
  finish order: P1 P0 P2 P3 P4
  deadlocked: none
  final Work = (9, 4, 6)
second example
  finish order: P1 P2 P3
  deadlocked: P0 P4
  final Work = (7, 2, 6)

Reading it. In the first example P0 asks for (2,1,1)(2, 1, 1) and only (1,1,1)(1, 1, 1) is free, so P1 (asking (0,1,0)(0, 1, 0)) goes first and returns (3,0,2)(3, 0, 2); then everyone else can finish: no deadlock. In the second example P1, P2 and P3 can finish, which leaves Work=(7,2,6)\text{Work} = (7, 2, 6). P0 and P4 each want three units of B and only two are free; the only other B units are held by P0 and P4 themselves, each waiting for the other: P0 and P4 are deadlocked. Process P2 asks for nothing, so it is not waiting at all.

EX 3The cheapest victims

Medium

For the graph above, try every set of processes as victims, remove them from the wait-for graph, and see which sets leave no cycle. The costs (work that would be lost) are P1: 3, P2: 5, P3: 2, P4: 4, P5: 1.

#include <stdio.h>
 
#define N 5
 
/* wait-for edges: P1 -> P2, P2 -> P3, P2 -> P4, P3 -> P1, P4 -> P5, P5 -> P4  (bit i is process P(i+1)) */
static const int edge[N][N] = {{0, 1, 0, 0, 0}, {0, 0, 1, 1, 0}, {1, 0, 0, 0, 0}, {0, 0, 0, 0, 1}, {0, 0, 0, 1, 0}};
static const int cost[N] = {3, 5, 2, 4, 1};
 
/* the graph induced by the processes that are NOT victims has no cycle iff repeatedly deleting a node without outgoing edges deletes them all */
static int acyclic_without(int victims) {
    int alive = ((1 << N) - 1) & ~victims, changed = 1;
    while (changed) {
        changed = 0;
        for (int i = 0; i < N; i++) {
            if (!(alive & (1 << i))) continue;
            int out = 0;
            for (int j = 0; j < N; j++)
                if ((alive & (1 << j)) && edge[i][j]) out = 1;
            if (!out) { alive &= ~(1 << i); changed = 1; }
        }
    }
    return alive == 0;
}
 
static int popcount(int x) { int c = 0; while (x) { c += x & 1; x >>= 1; } return c; }
 
int main(void) {
    int minimal[64], nmin = 0;
    for (int size = 1; size <= N; size++)
        for (int m = 1; m < (1 << N); m++) {
            if (popcount(m) != size || !acyclic_without(m)) continue;
            int proper = 0;
            for (int k = 0; k < nmin; k++)
                if ((minimal[k] & m) == minimal[k]) proper = 1;
            if (!proper) minimal[nmin++] = m;
        }
    int best = -1, bestcost = 1000;
    for (int k = 0; k < nmin; k++) {
        int c = 0;
        printf("abort");
        for (int i = 0; i < N; i++)
            if (minimal[k] & (1 << i)) { printf(" P%d", i + 1); c += cost[i]; }
        printf(": cost %d\n", c);
        if (c < bestcost) { bestcost = c; best = k; }
    }
    printf("single victims that work:");
    int any = 0;
    for (int i = 0; i < N; i++)
        if (acyclic_without(1 << i)) { printf(" P%d", i + 1); any = 1; }
    printf("%s\ncheapest: mask %d, cost %d\n", any ? "" : " none", minimal[best], bestcost);
    return 0;
}
abort P1 P4: cost 7
abort P2 P4: cost 9
abort P3 P4: cost 6
abort P1 P5: cost 4
abort P2 P5: cost 6
abort P3 P5: cost 3
single victims that work: none
cheapest: mask 20, cost 3

Reading it. Each cycle needs at least one victim, and there are two disjoint cycles, so one victim is never enough, and any set with one process from each cycle works: 3×2=63 \times 2 = 6 smallest sets. P2 on its own does not help, because it is on the first cycle only. The cheapest pair is P3 (cost 2) and P5 (cost 1), total 3. (Mask 20 is the bits for P3 and P5.) A real system would also weigh how much the work is worth, not only its cost, and would count how often a process has already been rolled back.

EX 4Unsafe is not deadlocked

GATE level

A system has the Allocation matrix of the animations and Available (1,1,1)(1, 1, 1), and nobody is waiting for anything (every Request is zero). Each process has declared that it may still claim (2,2,2)(2, 2, 2) more. Is the state safe? Is there a deadlock? The Banker's safety test compares Need =(2,2,2)= (2, 2, 2) with Work (1,1,1)(1, 1, 1): nobody can be guaranteed to finish, the state is unsafe. The detection algorithm compares the Request, which is zero: every process "can" finish, there is no deadlock. Both are right: unsafe says deadlock is possible if the processes really ask for their maximum; no deadlock says nothing is stuck now. The converse never fails: if the detection algorithm finds a deadlock, the state is unsafe for any maximum claims at least as large as the requests (the table of the page shows the three cases).

EX 5Which process finishes last?

GATE level

When a question gives Allocation and Request and asks for the order in which processes finish, check whether the order is forced. In the first matrix example, 72 of the 120 orders of the five processes are valid, and every process can be the last one, so "which process finishes last" has no answer there. It has an answer only when the requests form a chain: for instance, if the only unfinished process able to proceed at each stage is a different one, as when each process waits for exactly what the previous one returns. Compute it by running the algorithm and, at each step, listing all eligible processes, not only the first. And the set of deadlocked processes is always unique, even when the order is not.

Cheat box

Cheat box

  • Single instance: wait-for graph (Pi→PjP_i \to P_j: PiP_i waits for a resource held by PjP_j); deadlock iff a cycle.
  • Several instances: Work = Available; Finish[i] = (Allocation[i] = 0); find i with Finish[i] false and Request[i] <= Work; Work += Allocation[i]; repeat. Unfinished at the end: deadlocked.
  • Cost O(mn2)O(m n^2). Optimistic: assumes a non-waiting process asks for nothing more.
  • Banker's test uses Need (worst case) and says unsafe (possible); detection uses Request and says deadlocked (now). Deadlock ⇒\Rightarrow unsafe, not the converse.
  • When to run: at each blocked request, periodically, or when CPU utilisation drops.
  • Recovery: abort all, or one at a time; or preempt (victim, rollback, avoid starvation by counting rollbacks).
  • A deadlock needs a victim in EACH cycle.

Traps and GATE patterns

Connections

Detection resembles the safety algorithm of the Banker's page and works on the same kind of graph as resource allocation graphs. Terminating a victim is the process termination of the process lifecycle. The same idea appears in databases: a lock manager keeps a wait-for graph of transactions, finds the cycle and aborts a victim, as in two-phase locking.

PYQs

3 previous year questions from your book. I re-solved each one myself (3 by running or simulating, 0 by reasoning only); the book's key is shown next to my answer. All PYQs

GATE 2025 · MCQQ3 · page 330 of the book
P = {P1, P2, P3, P4} consists of all active processes
in an operating system. R = {R1, R2, R3, R4} consists
of single instances of distinct types of resources in the
system.

The resource allocation graph has the following
assignment and claim edges.

Assignment edges: R1 → P1, R2 → P2, R3 → P3, R4
→ P4 (the assignment edge R1 → P1 means resource
R1 is assigned to process P1 , and so on for others)

Claim edges: P1 → R2, P2 → R3, P3 → R1, P2 →
R4, P4 → R2 (the claim edge P1 → R2 means process
P1 is waiting for resource R2 , and so on for others)

Which
of
the
following
statement(s)
is/are
CORRECT?
  • (a)Aborting P1 makes the system deadlock free.
  • (b)Aborting P3 makes the system deadlock free
  • (c)Aborting P2 makes the system deadlock free
  • (d)Aborting P1 and P4 makes the system deadlock free.
GATE 2009 · MCQ · 2 marksQ16 · page 333 of the book
Consider a system with 4 types of resources R1 (3
units), R2 (2 units), R3 (3 units), R4 (2 units). A non-
preemptive resource allocation policy is used. At any
given instance, a request is not entertained if it cannot
be completely satisfied. Three processes P1, P2, P3
request the resources as follows if executed
independently.
Process P1:
t= 0 : requests
2 units of
R2
t= 1 : requests
1 unit of
R3
t= 3 : requests
2 units of
R1
t= 5 : releases
1 unit of
R2 and 1
unit
of
R1
t= 7 : releases
1 unit of
R3
t= 8 : requests
2 units of
R4
t= 10
:
Finishes
Process P2:
t= 0 : requests
2 units of
R3
t= 2 : requests
1 unit of
R4
t= 4 : requests
1 unit of
R1
t= 6 : releases
1 unit of
R3
t= 8 : Finishes
Process P3:
t= 0 : requests
1 unit of
R4
t= 2 : requests
2 units of
R1
t= 5 : releases
2 units of
R1
t= 7 : requests
1 unit of
R2
t= 8 : requests
1 unit of
R3
t= 9
:
Finishes

Which one of the following statements is TRUE if all
three processes run concurrently starting at time t = 0
?

OPERATING SYSTEMS
  • (a)All processes will finish without any deadlock.
  • (b)Only P1 and P2 will be in deadlock.
  • (c)Only P1 and P3 will be in deadlock.
  • (d)All three processes will be in deadlock.
GATE 2007 · MCQ · 2 marksQ18 · page 334 of the book
A single processor system has three resource types
X, Y, and Z, which are shared by three processes.
There are 5 units of each resource type.

The column alloc denotes the number of units of
each resource type allocated to each process, and
the column request denotes the number of units of
each resource type requested by a process in order
to complete execution.

Which of these processes will finish LAST?

alloc (X Y Z)
request (X Y Z)
P0
1 2 1
1 0 3
P1
2 0 1
0 1 2
P2
2 2 1
1 2 0
  • (a)P0
  • (b)P1
  • (c)P2
  • (d)None of the above, since the system is in a deadlock.

Self-check

Question 1

In a system with single-instance resources, P1 waits for a resource held by P2, P2 for one held by P3, and P3 for one held by P1. Is there a deadlock? Which single abort clears it?

Show answer

Yes: the wait-for graph is a cycle P1 → P2 → P3 → P1. Aborting any one of the three breaks it (and the freed resource goes to the process waiting for it).

Question 2

Two processes: Allocation P1=(1,0)P_1 = (1, 0), P2=(0,1)P_2 = (0, 1); Request P1=(0,1)P_1 = (0, 1), P2=(1,0)P_2 = (1, 0); Available (0,0)(0, 0). Deadlock?

Show answer

Work =(0,0)= (0, 0); neither Request fits ((0,1)(0,1) and (1,0)(1,0) exceed (0,0)(0,0)): both stay unfinished, so both are deadlocked.

Question 3

The same, but Available is (0,1)(0, 1). Deadlock?

Show answer

P1P_1's Request (0,1)(0, 1) fits Work (0,1)(0, 1): P1P_1 finishes, Work becomes (1,1)(1, 1), and then P2P_2 fits as well. No deadlock; the order is P1,P2P_1, P_2.

Question 4

Which matrix does the detection algorithm compare with Work, and which the Banker's safety test?

Show answer

Detection: the current Request. Safety test: Need == Max −- Allocation.

Question 5

Name two factors in choosing the victim, and the danger of always choosing by the lowest cost.

Show answer

For example the priority of the process and how much computation would be lost, or the number of resources it holds. Always taking the cheapest can starve the same process, which is rolled back again and again, so the number of rollbacks is added to the cost.

Revision card

One-screen revision

  • One instance: wait-for graph, cycle == deadlock. Several instances: detection algorithm on Allocation, Request, Available.
  • Work = Available; finished if holding nothing; pick Request[i] <= Work; Work += Allocation[i]; unfinished == deadlocked. Cost O(mn2)O(m n^2).
  • Banker's test: Need, unsafe == possible. Detection: Request, deadlocked == now.
  • Recovery: abort all or one at a time; preempt with rollback; count rollbacks against starvation.
  • A victim is needed on every cycle.

References

  • ·Silberschatz, Galvin, Gagne, Operating System Concepts, Chapter 8 Deadlocks (deadlock detection, recovery from deadlock)
  • ·Tanenbaum and Bos, Modern Operating Systems, Chapter 6 Deadlocks (detection and recovery)
  • ·Stallings, Operating Systems: Internals and Design Principles, Chapter 6 Concurrency: Deadlock and Starvation (deadlock detection, recovery)

Verification

The detection algorithm of the two matrix examples is run by scripts/gen-os-deadlock2-traces.py and shown step by step; scripts/enum-checks/os_deadlock2.py checks it on 6000 random states against an independent exhaustive search of finishing orders (the deadlocked set is the same for every order of picking), against the Banker's safety test when Max = Allocation + Request (6000 states; deadlock always implies unsafe, unsafe without deadlock occurs often), and finds the smallest sets of victims of the page's graph by trying every subset of the wait-for graph (two cycles, six smallest sets, cheapest {P3, P5}); it also counts that 72 of the 120 orders of the first example are valid finishing orders. tests/os-deadlock2-traces.test.ts re-implements the detection in TypeScript. The two C programs on the page are run by npm run verify:c. The victim costs are made-up numbers; the factors that decide a real choice are those the textbooks list. The algorithm is optimistic: it assumes a process that is not waiting asks for nothing more. A process that only waits behind a deadlocked process is also left unfinished and so is reported as deadlocked (the textbook's wording). Textbook chapter titles are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand. No defect found. Status raised to self-checked; verified needs a textbook or human check.

last verified: never