This module describes algorithms for "The Distributed Drinking Philosophers Problem," by which distributed agents share indivisible resources, such as exclusive access to files.
The description of the algorithm in this module is not as detailed as that given for dining philosophers. You can define a formal specification and carry out a detailed proof for this algorithm in almost exactly the same was as for dining philosophers.
The lifecycle of an agent is the same as in Dining Philosophers: (1) executing outside the critical section, (2) waiting to enter a critical section, and (3) executing in the critical section. The problem is identical to Dining Philosophers except for the transition to the critical section.
An agent executes outside the critical section without holding any resources. An agent may execute outside critical sections for ever, or it may start waiting to enter a critical section. When an agent starts waiting to enter a critical section it waits to get exclusive access to a nonempty subset of the resources. It continues waiting until it gets all the resources for which it waits. While it waits it does not change the subset of resources for which it waits. The agent continues to hold these resources when it executes in the critical section. An agent remains in a critical section for only a finite number of steps and then starts executing outside the critical section at which point it no longer needs resources.
For example, an agent needs exclusive access to a set of files for
it to execute in its next critical section, and it waits until it
is given this access. While it is in the critical section it
continues to have exclusive access to these files. When it is
executing outside the critical section it does not need access to
these files. Each time an agent starts to wait it may wait for a
different set of files.
The ideas in this module can be used to manage agents that require read or write access to files. Multiple agents can have read access to a file concurrently. While an agent has write access to a file no other agent can have access to the file. A homework problem deals with read/write access.
Drinking Philosophers
The drinking philosophers name is another example of an attempt at CS humor. A philosopher is in one of three states: tranquil, thirsty or drinking. A philosopher may remain tranquil for ever, or it may become thirsty for one or more beverages. It remains thirsty until it gets all the beverages for which it waits. Only when a thirsty philosopher gets all the beverages for which it waits does it start drinking. It continues to drinking all these beverages until it becomes tranquil again. Philosophers drink for only finite time.
A philosopher that enters the tranquil state remains in that state for
at least a constant \(\gamma\) amount of time. So, a philosopher can't
go from drinking to thirsty instantaneously or in an arbitrarily small
amount of time.
A beverage can be held by at most one philosopher. Imagine there's only one bottle of each beverage in the system, and philosophers send bottles to each other. One philosopher can drink vodka and cola while another philosopher drinks gin and tonic. However, one agent cannot drink vodka and cola while another drinks vodka and orange juice.
Messages
Agent Actions
Manager Actions
A manager has local variable \((hr, hp)\), which is the id of the agent to which the manager has most recently sent the beverage, and the priority of the request made by that agent. \(hr\) is an acronym for handling requestor, and \(hp\) is an acronym for handling priority. If the manager holds the beverage then this variable is empty (\(Null\)).A manager also maintains a priority queue of pending requests ordered by priority.
The actions of a manager are as follows.
The next diagram, stage 1, shows the state after Maya gets thirsty for tea and milk.
So she sends requests to the managers of tea and milk. The priority
of this request is 2. (We will discuss how priorities are obtained
later.)
Maya has received the milk beverage but tea is still in the
channel. So, Maya remains thirsty.
One way to assign priorities is as follows. Associated with each request is a timestamp which is the time read from the requestor's local clock at the instant at which the request is made. A request's timestamp does not change after the request is created.
A request's priority is a pair (timestamp, requestor's id), with priorities compared lexicographically, and lower values having higher priority. So, requests made earlier have higher priority than requests made later. The requestor's id is used to break ties.
What are the requirement's of agents' local clocks that ensure that all thirsty philosophers drink eventually?
A Minimum Requirement on Clocks
An agent's clock must tick forward between successive requests by the agent. If the agent's clock remained stuck at the same value for ever then the agent using that clock may get the highest priority for ever, and go through tranquil, thirsty, drinking cycles infinitely which makes other agents remain tranquil for ever.Assume that each clock reading is an integer: for example, the number of picoseconds since January 1, 1900. (NTP units are \(2^{-32}\) of a second.) The clock ticks forward between successive requests by the same philosopher because the philosopher remains in tranquil state for at least \(\gamma\) units of time where \(\gamma\) is a positive constant. Treat each agent's clock tick as an event.
Local clocks of agents may drift apart, but let's assume that the magnitude of the difference between clock readings of different agents is bounded.
Even if the philosopher makes a new request when its clock ticks forward by one unit, eventually the timestamp of a request from that philosopher will exceed \(T\) for any value of \(T\). This ensures that while a philosopher is thirsty, another philosopher cannot overtake it for ever.
Let's prove that a thirsty philosopher \(v\) with a request with timestamp \(T\) drinks eventually. Let \(t[i]\) be the reading of philosopher \(i\)'s clock.
Each agent's clock reading eventually exceeds \(T\)
We observe that each agent's clock ticks forward:\( \forall i, \tau: (t[i] = \tau) \leadsto (t[i] \geq \tau + 1) \)
Therefore, each \(i\)'s clock eventually reads a value greater than \(T\), using transitivity of \(\leadsto\).
\( \forall i, \tau: (t[i] = \tau) \leadsto (t[i] > T) \)
From the previous formula, and taking disjunction over all values of \(\tau\):
\( \forall i: \quad true \leadsto (t[i] > T) \)
Because clock readings never go back in time, \(t[i] > T\) is stable, and therefore:
\( \forall i: \quad true \leadsto always(t[i] > T) \)
Let \(Q\) be the predicate that all clocks read values that exceed \(T\):
\(Q = \forall i: t[i] > T\).
From the above formula and because \(X \leadsto always(Y)\) and \(X \leadsto always(Y')\) allows us to deduce \(X \leadsto always(Y \vee Y')\)
\( true \leadsto always(Q) \)
The number of pending requests with timestamps less than \(T\) decreases
From the above formula:
\( v.thirsty \; \leadsto \; (v.drinking \wedge Q) \vee (v.thirsty \wedge Q) \)
Next we will prove that if \(v\) is thirsty and all clocks read values that exceed \(T\), then \(v\) will drink eventually:
\( (v.thirsty \wedge Q) \leadsto v.drinking \)
Let \(req\) be the number of pending requests with timestamps less than \(T\). We will prove
\( (v.thirsty \wedge Q \wedge (req = k) \; \leadsto \; v.drinking \vee (v.thirsty \wedge Q \wedge (req < k)) \)
This proof is straightforward: The request with the lowest timestamp gets all the resources it needs.
\( (v.thirsty \wedge Q \wedge (req = 0) \; \leadsto \; v.drinking \)
The result follows using the rules for variant functions.
K. Mani Chandy, Emeritus Simon Ramo Professor, California Institute of Technology