Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Operating Systems  Module 6 – Deadlocks

Preventing deadlocks by design

Make deadlock impossible by ruling out one Coffman condition: one global lock order, all-at-once requests, try-lock with back-off, or enough units.

  • Intermediate
  • 20 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Clang 21.0.0 and GCC 16.2.0
  • By MySmartCoPilot
Free core, Pro depth: 7 of 20 minutes free
Free: One broken condition is enough, and each has a price · A global lock order makes the cycle impossible · Enough units leave no room for deadlock · Practise: the fewest units for any maximums. With a pass: 3 in-depth parts and 3 interview answers, about 13 minutes.

What you will learn

  • Prevent deadlock by denying each Coffman condition, and judge the cost of each
  • Apply one global lock order, by rank or by address, to code that takes several locks
  • Compute the fewest units of a resource that rule out deadlock
  • Choose between try-lock with back-off and taking every lock up front

Before you start

Eight threads in the program below made 800,000 transfers between 16 accounts, each transfer holding two account locks at once, and none of them ever deadlocked. No lucky timing made that safe: every thread took the lock of the lower-numbered account first, so the circular wait that a deadlock needs could never form.

That is deadlock prevention: Designing a system so that one of the four Coffman conditions can never hold, which makes deadlock impossible whatever the timing.. Every deadlock needs all four Coffman conditions, so a design that makes any one of them impossible can never deadlock, whatever the timing. Each condition can be broken, and each costs something different: locks held before they are needed, work thrown away and redone, or a rule that every line of the program must follow.

One broken condition is enough, and each has a price

Each Coffman condition has its own cure. Sharing the resource removes mutual exclusion: read-only data, or one owner thread that serves every request, leaves nobody waiting for it. Taking every lock up front removes hold and wait: a thread asks for all its locks in one step and holds nothing while it waits. try-lock: A lock call that returns at once and says whether it got the lock, instead of waiting for it; pthread_mutex_trylock and Go's Mutex.TryLock are examples. with back-off breaks no preemption: a thread that cannot get its next lock gives back what it holds and tries again later. One lock ordering: Preventing deadlock by giving every lock a rank and making every thread take the locks it holds together in increasing rank, so that no chain of waits can close into a cycle. removes circular wait: every thread takes its locks in increasing order, so no chain of waits can close.

Dijkstra’s five dining philosophers show the deadlock and one of these cures. When every philosopher picks up the left fork first, all five can hold one fork and wait for the other, the deadly embrace. Taking both forks in one indivisible step removes hold and wait, and Dijkstra set even that aside because a philosopher could then starve. Answer the questions below for a resource of your own:

Interactive · answer and compare Which condition can your design rule out? Answer four questions about the resource and the code that uses it. The table marks the ways of preventing deadlock that fit, with the reason.
Can every user of the resource use it at the same time, for example because it is only read?
Does each thread know every lock it will need before it starts?
Can a thread give back what it holds and safely redo the work it did?
Can all the code that takes these locks agree on one order for them?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Four ways to prevent deadlock, one per Coffman condition, against what each one costs
Way to prevent Cost at run time What the code must know What can still go wrong
Share the resource Rules out: mutual exclusion Good readers never wait for each other Mixed only for data that is read, or that one owner thread serves Mixed most data that changes cannot be shared
Take every lock up front Rules out: hold and wait Poor locks are held long before they are used, so others wait Poor every lock the thread will need before it starts Mixed a thread that needs many locks may wait a long time
Try-lock and back off Rules out: no preemption Mixed work done under a lock is redone after each retry Good nothing in advance Poor threads that retry in step can livelock
One global lock order Rules out: circular wait Good nothing beyond taking the locks Mixed one order for every pair of locks that a thread can hold together Mixed one path that takes the locks the other way brings the risk back
How the answers decide
  • “Can every user of the resource use it at the same time, for example because it is only read?” No, one user at a time: rules out Share the resource. A resource that one user at a time must have keeps mutual exclusion, so sharing is not available for it.
  • “Can every user of the resource use it at the same time, for example because it is only read?” Yes, it can be shared: adds a caveat to Take every lock up front, Try-lock and back off and One global lock order. Data that every user can read at once needs no lock to wait for, which beats any rule about how to wait.
  • “Does each thread know every lock it will need before it starts?” No, it finds out as it runs: rules out Take every lock up front. A thread cannot ask for all its locks in one step when it learns what it needs only as it runs.
  • “Can a thread give back what it holds and safely redo the work it did?” No, it has side effects: rules out Try-lock and back off. Giving a lock back means undoing what was done under it, and a sent message or a written file cannot be taken back.
  • “Can all the code that takes these locks agree on one order for them?” Yes, one order fits all of it: adds a caveat to Take every lock up front and Try-lock and back off. Once the code agrees on an order, the order costs nothing at run time, while the other two keep their waiting or their retries.
  • “Can all the code that takes these locks agree on one order for them?” No, some of it is not ours: adds a caveat to One global lock order. An order that some code does not follow is broken by the first path that takes the locks the other way round.

Order first

Reach for one global lock order first. It costs nothing at run time and asks only that every path follows it; keep the other three for locks that no single order can cover.

Checkpoint

Check: The four ways to prevent deadlock and their costs

  1. Question 1 of 2 A thread asks for every lock it will need in one step and holds none of them while it waits. Which Coffman condition can no longer hold?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Hold and wait

    All-at-once requests remove hold and wait, at the price of holding every lock from the start of the work.

  2. Question 2 of 2 Threads no longer lock the printer; they put jobs on a queue, and one owner thread prints them in turn. For the threads that send jobs, which condition has the design removed?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Mutual exclusion, because no sending thread ever needs the printer for itself

    One owner thread that serves every request turns an exclusive resource into a shared service, which is how sharing removes mutual exclusion for resources that cannot be read in parallel.

A global lock order makes the cycle impossible

Number every lock, and let each thread take locks only in increasing number. A thread that holds lock i can then wait only for a lock numbered above i. Follow any chain of waiting threads and the numbers keep rising, so the chain can never return to its start: there is no cycle, and so no deadlock. In ordered_locks.c the number is the account number, and the thread locks the two accounts of a transfer from the lower to the higher:

ordered_locks.c: 800,000 transfers on 8 threads, lower account first C · ordered/ordered_locks.c
// Eight threads move money between 16 accounts. Every transfer locks its two
// accounts in one global order, the lower account number first, so no cycle of
// waiting threads can ever form.
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>

enum { ACCOUNTS = 16, THREADS = 8, TRANSFERS = 100000 };

static struct {
  pthread_mutex_t lock;
  int64_t balance;
} accounts[ACCOUNTS];

typedef struct {
  uint64_t seed;
  long downwards; // transfers to a lower account number
} worker;

// SplitMix64: the same pseudo-random numbers on every machine.
static uint64_t next_random(uint64_t *state) {
  uint64_t z = (*state += 0x9e3779b97f4a7c15u);
  z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9u;
  z = (z ^ (z >> 27)) * 0x94d049bb133111ebu;
  return z ^ (z >> 31);
}

static void transfer(int from, int to, int64_t amount) {
  const int first = from < to ? from : to; // the global order
  const int second = from < to ? to : from;
  pthread_mutex_lock(&accounts[first].lock);
  pthread_mutex_lock(&accounts[second].lock);
  accounts[from].balance -= amount;
  accounts[to].balance += amount;
  pthread_mutex_unlock(&accounts[second].lock);
  pthread_mutex_unlock(&accounts[first].lock);
}

static void *run(void *arg) {
  worker *w = arg;
  for (int i = 0; i < TRANSFERS; i++) {
    const int from = (int)(next_random(&w->seed) % ACCOUNTS);
    int to = (int)(next_random(&w->seed) % (ACCOUNTS - 1));
    if (to >= from)
      to++; // any account but the source
    const int64_t amount = (int64_t)(next_random(&w->seed) % 100) + 1;
    transfer(from, to, amount);
    w->downwards += to < from;
  }
  return NULL;
}

static int64_t total(void) {
  int64_t sum = 0;
  for (int i = 0; i < ACCOUNTS; i++)
    sum += accounts[i].balance;
  return sum;
}

int main(void) {
  for (int i = 0; i < ACCOUNTS; i++) {
    pthread_mutex_init(&accounts[i].lock, NULL);
    accounts[i].balance = 1000000;
  }
  const int64_t before = total();
  pthread_t threads[THREADS];
  worker workers[THREADS];
  for (int i = 0; i < THREADS; i++) {
    workers[i] = (worker){.seed = (uint64_t)i + 1, .downwards = 0};
    pthread_create(&threads[i], NULL, run, &workers[i]);
  }
  long downwards = 0;
  for (int i = 0; i < THREADS; i++) {
    pthread_join(threads[i], NULL);
    downwards += workers[i].downwards;
  }
  printf("%d threads made %d transfers between %d accounts\n", THREADS,
         THREADS * TRANSFERS, ACCOUNTS);
  printf("%ld moved money to a lower account number, so the thread locked the "
         "destination first\n",
         downwards);
  printf("total before %" PRId64 ", total after %" PRId64 "\n", before,
         total());
  return 0;
}

Output

8 threads made 800000 transfers between 16 accounts
399757 moved money to a lower account number, so the thread locked the destination first
total before 16000000, total after 16000000

Recorded with Apple clang version 21.0.0 (clang-2100.3.34.2) on macOS 26 arm64. To run it yourself: clang -std=c23 -O2 -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined ordered_locks.c -o ordered_locks && ./ordered_locks

All 800,000 transfers finished and the 16 accounts still held 16,000,000 between them. In 399,757 transfers the money moved to a lower-numbered account, so the thread locked the destination before the source: the order follows the numbers, never the direction of the money. Step through the two transfers of the previous lesson under the same rule:

Interactive · step by step The same two transfers, lower account number first Step through the two transfers when both lock account 17 before account 42. Watch who waits, and what the waiting thread holds.
The same two transfers, lower account number first, step 6 of 6. Showing Thread A (finished), Lock of account 42 (held by B), Lock of account 17 (held by B), Thread B (finished after A).held byheld byThread AfinishedLock of account 42held by BLock of account 17held by BThread Bfinished after A

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. Both start with lock 17. A moves money from 17 to 42 and B from 42 to 17. Both now lock the lower account number first, so both begin by asking for lock 17, whichever way their money moves.
  2. A takes lock 17. A gets lock 17 first. It is the only lock either thread may take first, so whoever holds it is the only thread that can be inside a transfer.
  3. B waits for lock 17. B asks for lock 17 and waits. It holds nothing while it waits, so no thread can be waiting for B.
  4. A takes lock 42. Lock 42 is free, because a thread takes 42 only after 17 and A holds 17. A holds both locks and makes its transfer.
  5. A finishes. A releases both locks. The only waiting thread waited for a lock that a running thread held, so the wait ends.
  6. B goes on. B takes 17 and then 42 and finishes too. With the order, a thread that waits holds only lower-numbered locks than the one it wants, so no chain of waits can come back to its start.

Real programs name the order in a few ways. Ranks give every class of lock a fixed number. A lock hierarchy: A lock order by levels: locks are grouped into numbered levels, and a thread that holds a lock of one level may take only locks of higher levels. groups locks into levels and lets a thread holding one level take only higher ones. Locks created at run time, such as one per account, are ordered by an id or an address. A lock-order checker records which lock each path takes inside which, and reports any two locks taken in both orders.

Key idea

When every thread takes its locks in one increasing order, every chain of waits climbs, so it can never close into the cycle that a deadlock needs.

Checkpoint

Check: A global lock order

  1. Question 1 of 2 Under the rule of ordered_locks.c, a thread moves money from account 42 to account 17. Which lock does it take first?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The lock of account 17, the destination

    In the run, 399,757 of the 800,000 transfers locked their destination first for exactly this reason.

  2. Question 2 of 2 Every thread takes locks only in increasing number. Why can no cycle of waiting threads form?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A thread holding lock i waits only for a lock above i, so along any chain of waits the numbers keep rising and never return to the start

    The numbers act as an invariant: every arrow of the wait-for graph goes from a lower held lock to a higher wanted one, so the graph has no cycle.

Enough units leave no room for deadlock

Some resources come in identical units, such as tape drives or connections in a pool. If n processes each need at most k units, n(k − 1) + 1 units rule out deadlock: even when each process holds k − 1 and waits for its last, one unit is left, so some process finishes and returns its units. Step through the worst moment for four processes that each need up to 3 drives, with 4 × 2 + 1 = 9 drives in the pool:

Interactive · step by step Nine drives for four processes that need up to 3 Step through the worst moment for the pool, with every process one drive short, and watch the spare drive end it.
Nine drives for four processes that need up to 3, step 5 of 5. Showing Tape drives (9 drives, 9 free), P1 (finished), P2 (finished), P3 (finished), P4 (finished).lendslendslendslendsTape drives9 drives, 9 freeP1finishedP2finishedP3finishedP4finished

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. Nine drives, four processes. Four processes each need up to 3 drives and ask for them one at a time. The bound gives the pool 4 × (3 − 1) + 1 = 9 drives.
  2. Everyone one drive short. The worst moment comes when every process holds 2 drives and waits for its third. That is the most the four can hold while all of them are short, and it takes 8 drives.
  3. The spare drive. One drive is still free, so it goes to the first process that asks, here P1. P1 now holds its maximum of 3.
  4. P1 finishes. P1 finishes and returns all 3 of its drives, so 3 are free, one for each process that is still waiting.
  5. Everyone finishes. P2, P3 and P4 take their third drives, finish and return them. However the requests arrive, the four can never all be one drive short with none free, so 9 drives rule out deadlock.

With every process one drive short, 8 drives were taken and the ninth let P1 finish, which freed 3 for the others. A process that needs a single unit never holds one while it waits, so it adds nothing to the count: any number of them can share one drive without deadlock.

Checkpoint

Check: The resource bound n(k − 1) + 1

  1. One question Seven processes each need at most 3 units of a resource and ask for them one at a time. What is the fewest number of units that rules out deadlock?

    Type a number.

    Show the answer: One question

    Answer: 15 units

    n(k − 1) + 1 = 7 × 2 + 1 = 15. With all seven one unit short, 14 units are taken, and the fifteenth lets one of them reach its maximum, finish and return its units.

Practise: the fewest units for any maximums

Write the bound for processes whose maximums differ, including processes that need one unit or none. The sample tests run in your browser.

Exercise · Medium · Python

The fewest units that rule out deadlock

Processes share the units of one resource, such as tape drives or connections, and ask for them one unit at a time. maxes[i] is the most units process i will ever hold at once; a process that reaches its maximum finishes and returns everything it holds, and a process with a maximum of 0 never asks for a unit.

Write min_units(maxes), which returns the smallest number of units with which these processes can never deadlock, whatever order their requests come in. With n identical processes of maximum k the answer is n(k − 1) + 1, so min_units([3, 3, 3, 3]) is 9; your function must also handle processes with different maximums, processes that need one unit or none, and an empty list.

Starter code · units.py

def min_units(maxes):
    """Returns the fewest units of one resource with which these processes can never deadlock.

    maxes[i] is the most units process i ever holds at once; it asks for them one at a time.
    """
    # Replace this line with your code.
    return len(maxes) * (max(maxes, default=0) - 1) + 1
The sample tests · test_units.py
from units import min_units


def test_identical_processes():
    """four processes that each need up to 3 units: 4 x 2 + 1"""
    assert min_units([3, 3, 3, 3]) == 9


def test_different_maximums():
    """processes with maximums 2, 3 and 4"""
    assert min_units([2, 3, 4]) == 7


def test_one_unit_each():
    """a process that needs one unit never holds one while it waits"""
    assert min_units([1, 1, 1]) == 1


def test_one_process():
    """a single process only needs its own maximum"""
    assert min_units([5]) == 5


def test_a_process_that_needs_nothing():
    """a process with a maximum of 0 never asks for a unit"""
    assert min_units([0, 4]) == 4


def test_no_processes():
    """with no process, no unit is needed"""
    assert min_units([]) == 0
A hint

A deadlock needs every process to be stuck at the same moment: each one short of its maximum and no unit free. Ask how many units the processes can hold in total while every one of them is still short, and add one.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Practice that adapts to you

Practise this lesson

9 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 9 Which way of preventing deadlock removes circular wait?

    Choose one answer.

    Show the answer: Question 1 of 9

    Answer: Taking locks in one global order

    Each of the four cures targets one condition, and one is enough.

  2. Question 2 of 9 A thread learns which locks it needs only as it runs, and every lock may be held for a long time. Which cure fits worst?

    Choose one answer.

    Show the answer: Question 2 of 9

    Answer: Taking every lock up front

    The choice table rules up-front requests out as soon as a thread finds out its needs as it runs.

  3. Question 3 of 9 A thread sends a payment message to a bank while it holds a lock, then needs a second lock. Why is try-lock with back-off a poor cure here?

    Choose one answer.

    Show the answer: Question 3 of 9

    Answer: Backing off means undoing the work done under the first lock, and a sent message cannot be taken back

    Preemption, even of oneself, only suits work that can be undone or repeated, which is why the choice table asks about side effects.

  4. Question 4 of 9 What rule makes a global lock order work?

    Choose one answer.

    Show the answer: Question 4 of 9

    Answer: Every thread takes the locks it holds together in increasing order of their ranks

    Only the order of taking matters, and it has to hold on every path that holds two locks together.

  5. Question 5 of 9 About half of the transfers in ordered_locks.c locked their destination account first. Why is that correct and not a bug?

    Choose one answer.

    Show the answer: Question 5 of 9

    Answer: The order is by account number, so a transfer to a lower number must lock the destination first

    The program counted 399,757 such transfers out of 800,000 and all of them finished.

  6. Question 6 of 9 Every module takes config before cache except one new plugin, which takes cache before config. What has changed?

    Choose one answer.

    Show the answer: Question 6 of 9

    Answer: A cycle is possible again, between the plugin and any path that takes config before cache

    An order protects only while every path follows it, which is why real code checks it automatically.

  7. Question 7 of 9 n processes each need at most k units of one resource. How many units guarantee that they cannot deadlock?

    Choose one answer.

    Show the answer: Question 7 of 9

    Answer: n(k − 1) + 1

    Four processes that need 3 each need 4 × 2 + 1 = 9 units.

  8. Question 8 of 9 Five processes each need at most 4 units. How many units are needed to rule out deadlock?

    Type a number.

    Show the answer: Question 8 of 9

    Answer: 16 units

    5 × (4 − 1) + 1 = 16: five processes can hold 3 units each, 15 in all, while every one of them is still short.

  9. Question 9 of 9 Four processes each need at most 3 drives, and the system has 8. What can happen?

    Choose one answer.

    Show the answer: Question 9 of 9

    Answer: Each can hold 2 drives and wait for a third, with no drive free, which is a deadlock

    It is the worst moment of the nine-drive pool without its spare drive: every process holds 2, all 8 drives are taken, and no process can reach its maximum.

The resource bound, proved and checked against every allocation Pro: opens with a Pro, Premium or Ultimate pass

A pool of identical units can be made deadlock-free by owning enough of them. This part proves the bound, extends it to processes with different maximums, checks it against every possible allocation, and works through the numerical questions it produces.

In this part:

  • Why the bound always suffices, and one unit fewer never does
  • Different maximums: one short of every maximum, plus one
  • Every allocation checked by brute force

1 runnable example (Python) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Lock order in real code: ranks, addresses, hierarchies and a checker Pro: opens with a Pro, Premium or Ultimate pass

A global order is easy to state and easy to break, because the second lock is often taken far from the first, in another module or a callback. This part shows how real code numbers its locks, how it orders locks created at run time, and how a checker catches a broken order before it deadlocks.

In this part:

  • Ranks for fixed locks, levels for layers
  • Ordering locks created at run time, by id or by address
  • Checking the order across every code path

1 runnable example · about 8 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Try-lock with back-off and all-at-once requests, measured Pro: opens with a Pro, Premium or Ultimate pass

When no order can be agreed, a thread can refuse to wait while it holds a lock: it asks for every lock at once, or it gives back what it has and tries again. This part measures what each one costs, including the livelock that try-lock invites and the waiting that all-at-once requests add.

In this part:

  • Try-lock and back off: the livelock it risks, measured
  • What libraries do with try-lock
  • Taking every lock up front: the cost in waiting, with numbers

1 runnable example (Python) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

The warm-up comes with its full answer:

  1. Fresher to mid · Warm-up

    Why does taking locks in one global order prevent deadlock?

    A deadlock needs a circular wait: a closed chain of threads in which each waits for a lock that the next one holds. Give every lock a rank and let each thread take the locks it holds together only in increasing rank. A thread that holds a lock of rank i can then wait only for a lock of a higher rank, so along any chain of waiting threads the ranks keep rising. A rising chain can never come back to the thread it started from, so no cycle can form, and without a cycle there is no deadlock. The order costs nothing at run time; what it needs is that every path follows it, including paths in other modules and callbacks, which is why real code writes the ranks down and checks them.

Three harder questions follow from this lesson: transfers between accounts created at run time, the units a pool needs and a fix that burns processor time. Choose a first move for two of them:

Checkpoint

Your first decision

  1. Question 1 of 2 Accounts are objects created at run time, and transfers between any two of them run on many threads. What is your first design decision for locking them?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Give every account a stable id and always lock the lower id first

    An order by id is free at run time; the cases left to decide are a transfer to the same account and equal ids.

  2. Question 2 of 2 A team fixed a deadlock by making threads release and retry when their second lock is taken. Under load the processors are busy and throughput has fallen. What do you suspect first?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Threads retrying in step, a livelock, where every release and retry burns processor time without progress

    Of the four cures, try-lock with back-off is the one whose failure is busy threads; a global order would remove the retries altogether.

  1. Fresher to mid

    Write the locking for a transfer between two bank accounts that never deadlocks, when accounts are objects created at run time and transfers run in both directions on many threads. What do you order the locks by, and which special cases must the code handle?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A print server has a pool of identical printers. Six jobs each need up to 4 printers at once and ask for them one at a time. What is the fewest number of printers that guarantees no deadlock, and why does one printer fewer fail?

    Model answer, Pro. First step and follow-ups

  3. Mid to senior

    A team fixed a deadlock by making each thread release its first lock and retry whenever its second lock is busy. Since then, at peak load, processor use has doubled and throughput has fallen by a third. Explain what is happening and what you would change.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • One Coffman condition made impossible is enough to prevent deadlock, and each of the four costs something different.
  • A global lock order costs nothing at run time: every chain of waits climbs, so it cannot close.
  • n processes needing at most k units each cannot deadlock with n(k − 1) + 1 units.

Terms in this lesson

deadlock prevention
Designing a system so that one of the four Coffman conditions can never hold, which makes deadlock impossible whatever the timing.
lock hierarchy
A lock order by levels: locks are grouped into numbered levels, and a thread that holds a lock of one level may take only locks of higher levels.
lock ordering
Preventing deadlock by giving every lock a rank and making every thread take the locks it holds together in increasing rank, so that no chain of waits can close into a cycle.
try-lock
A lock call that returns at once and says whether it got the lock, instead of waiting for it; pthread_mutex_trylock and Go's Mutex.TryLock are examples.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.