** NOTE: ** These notes are adapted from those of
Allan Gottlieb, and are
reproduced here with his permission.

================ Start Lecture #6
(Feb. 12)

To repeat: A **deadlock** occurs when a every member of a set of
processes is waiting for an event that can only be caused
by a member of the set.

Often the event waited for is the release of a resource.

The following four conditions (Coffman; Havender) are
*necessary* but *not* sufficient for deadlock. Repeat:
They are **not** sufficient.

- Mutual exclusion: A resource can be assigned to at most one process at a time (no sharing).
- Hold and wait: A processing holding a resource is permitted to request another.
- No preemption: A process must release its resources; they cannot be taken away.
- Circular wait: There must be a chain of processes such that each member of the chain is waiting for a resource held by the next member of the chain.

On the right is the **Resource Allocation Graph**,
also called the **Reusable Resource Graph**.

- The processes are circles.
- The resources are squares.
- An arc (directed line) from a process P to a resource R signifies that process P has requested (but not yet been allocated) resource R.
- An arc from a resource R to a process P indicates that process P has been allocated resource R.

There are four strategies used for dealing with deadlocks.

- Ignore the problem
- Detect deadlocks and recover from them
- Avoid deadlocks by carefully deciding when to allocate resources.
- Prevent deadlocks by violating one of the 4 necessary conditions.

The ``put your head in the sand approach''.

- If the likelihood of a deadlock is sufficiently small and the cost of avoiding a deadlock is sufficiently high it might be better to ignore the problem. For example if each PC deadlocks once per 100 years, the one reboot may be less painful that the restrictions needed to prevent it.
- Clearly not a good philosophy for nuclear missile launchers.
- For embedded systems (e.g., missile launchers) the programs run are fixed in advance so many of the questions Tanenbaum raises (such as many processes wanting to fork at the same time) don't occur.

Consider the case in which there is **only one**
instance of each resource.

- So a request can be satisfied by only one specific resource.
- In
**this case**the 4 necessary conditions for deadlock are also sufficient. - Remember we are making an assumption (single unit resources) that is often invalid. For example, many systems have several printers and a request is given for ``a printer'' not a specific printer. Similarly, one can have many tape drives.
- So the problem comes down to finding a directed cycle in the resource
allocation graph. Why?

Answer: Because the other three conditions are either satisfied by the system we are studying or are not in which case deadlock is not a question. That is, conditions 1,2,3 are conditions on the system in general not on what is happening right now.

To find a directed cycle in a directed graph is not hard. The algorithm is in the book. The idea is simple.

- For each node in the graph do a depth first traversal (hoping the graph is a DAG (directed acyclic graph), building a list as you go down the DAG.
- If you ever find the same node twice on your list, you have found a directed cycle and the graph is not a DAG and deadlock exists among the processes in your current list.
- If you never find the same node twice, the graph is a DAG and no deadlock occurs.
- The searches are finite since the list size is bounded by the number of nodes.

This is more difficult.

- The figure on the right shows a resource allocation graph with multiple unit resources.
- Each unit is represented by a dot in the box.
- Request edges are drawn to the box since they represent a request for any dot in the box.
- Allocation edges are drawn from the dot to represent that this unit of the resource has been assigned (but all units of a resource are equivalent and the choice of which one to assign is arbitrary).
- Note that there is a directed cycle in black, but there is no deadlock. Indeed the middle process might finish, erasing the magenta arc and permitting the blue dot to satisfy the rightmost process.
- The book gives an algorithm for detecting deadlocks in this more
general setting. The idea is as follows.
- look for a process that might be able to terminate (i.e., all its request arcs can be satisfied).
- If one is found pretend that it does terminate (erase all its arcs), and repeat step 1.
- If any processes remain, they are deadlocked.

- We will soon do in detail an algorithm (the Banker's algorithm) that has some of this flavor.

Perhaps you can temporarily preempt a resource from a process. Not likely.

Database (and other) systems take periodic checkpoints. If the system does take checkpoints, one can roll back to a checkpoint whenever a deadlock is detected. Somehow must guarantee forward progress.

Can always be done but might be painful. For example some processes have had effects that can't be simply undone. Print, launch a missile, etc.

**Remark:**
We are doing 3.6 before 3.5 since 3.6 is easier.

Attack one of the coffman/havender conditions

Idea is to use spooling instead of mutual exclusion. Not possible for many kinds of resources

Require each processes to request all resources at the beginning
of the run. This is often called **One Shot**.

Normally not possible.

Establish a fixed ordering of the resources and require that they be requested in this order. So if a process holds resources #34 and #54, it can request only resources #55 and higher.

It is easy to see that a cycle is no longer possible.

Let's see if we can tiptoe through the tulips and avoid deadlock states even though our system does permit all four of the necessary conditions for deadlock.

An *optimistic* resource manager is one that grants every
request as soon as it can. To avoid deadlocks with all four
conditions present, the manager must be smart not optimistic.

- We have two processes H (horizontal) and V.
- The origin represents them both starting.
- Their combined state is a point on the graph.
- The parts where the printer and plotter are needed by each process are indicated.
- The dark green is where both processes have the plotter and hence execution cannot reach this point.
- Light green represents both having the printer; also impossible.
- Pink is both having both printer and plotter; impossible.
- Gold is possible (H has plotter, V has printer), but you can't get there.
- The upper right corner is the goal both processes finished.
- The red dot is ... (cymbals) deadlock. We don't want to go there.
- The cyan is safe. From anywhere in the cyan we have horizontal and vertical moves to the finish line without hitting any impossible area.
- The magenta interior is very interesting. It is
- Possible: each processor has a different resource
- Not deadlocked: each processor can move within the magenta
- Deadly: deadlock is unavoidable. You will hit a magenta-green boundary and then will no choice but to turn and go to the red dot.

- The cyan-magenta border is the danger zone
- The dashed line represents a possible execution pattern.
- With a uniprocessor no diagonals are possible. We either move to the right meaning H is executing or move up indicating V.
- The trajectory shown represents.
- H excuting a little.
- V excuting a little.
- H executes; requests the printer; gets it; executes some more.
- V executes; requests the plotter.

- The crisis is at hand!
- If the resource manager gives V the plotter, the magenta has been entered and all is lost. ``Abandon all hope yee who enter here'' --Dante.
- The right thing to do is to deny the request, let H execute moving horizontally under the magenta and dark green. At the end of the dark green, no danger remains, both processes will complete successfully. Victory!
- This procedure is not practical for a general purpose OS since it requires knowing the programs in advance. That is, the resource manager, knows in advance what requests each process will make and in what order.

Avoiding deadlocks given some extra knowledge.

- Not surprisingly, the resource manager knows how many units of each resource it had to begin with.
- Also it knows how many units of each resource it has given to each process.
- It would be great to see all the programs in advance and thus know all future requests, but that is asking for too much.
- Instead, each process when it starts gives its maximum usage.
That is each process at startup states, for each resource, the maximum
number of units it can possibly ask for. This is called the
**claim**of the process.- If during the run the process asks for more than its claim, abort it.
- If it claims more than it needs, the result is that the resource manager will be more conservative than need be and there will be more waiting.

Give an example of all four possibilities. A state that is

- Safe and deadlocked--not possible
- Safe and not deadlocked
- Not safe and deadlocked
- Not safe and not deadlocked--interesting

A manager can determine if a state is safe.

- Since the manager know all the claims, it can determine the maximum amount of additional resources each process can request.
- The manager knows how many units of each resource it has left.

The manager then follows the following procedure, which is part of
**Banker's Algorithms** discovered by Dijkstra, to
determine if the state is safe.

- If there are no processes remaining, the state is
**safe**.

- Seek a process P whose max additional requests is less than
what remains (for each resource).
- If no such process can be found, then the state is
**not safe**. - The banker (manager) knows that if it refuses all requests
excepts those from P, then it will be able to satisfy all
of P's requests. Why?

Look at how P was chosen.

- If no such process can be found, then the state is
- The banker now pretends that P has terminated (since the banker
knows that it can guarantee this will happen). Hence the banker
pretends that all of P's currently held resources are returned. This
makes the banker richer and hence perhaps a process that was not
eligible to be chosen as P previously, can now be chosen.

- Repeat these steps.

process | claim | current |
---|---|---|

X | 3 | 1 |

Y | 11 | 5 |

Z | 19 | 10 |

total | 16 |

- One resource type R with 22 unit
- Three processes X, Y, and Z with claims 3, 11, and 19 respectively.
- Currently the processes have 1, 5, and 10 units respectively.
- Hence the manager currently has 6 units left.
- Also note that the max additional needs for processes are 2, 6, 9
- So the manager cannot assure (with its
**current**remaining supply of 6 units) that Z can terminate. But that is**not**the question. - This state is safe
- Use 2 units to satisfy X; now the manager has 7 units.
- Use 6 units to satisfy Y; now the manager has 12 units.
- Use 9 units to satisfy Z; done!

process | claim | current |
---|---|---|

X | 3 | 1 |

Y | 11 | 5 |

Z | 19 | 12 |

total | 18 |

Start with example 1 and assume that Z now requests 2 units and we grant them.

- Currently the processes have 1, 5, and 12 units respectively.
- The manager has 4 units.
- The max additional needs are 2, 6, and 7.
- This state is unsafe
- Use 2 unit to satisfy X; now the manager has 5 units.
- Y needs 6 and Z needs 7 so we can't guarantee satisfying either

- Note that we were able to find a process that can terminate (X) but then we were stuck. So it is not enough to find one process. Must find a sequence of all the processes.