The Dining Philosophers Problem is a standard example of concurrent
programming. The idea is that a group of
To keep the problem small, let's assume we have three philosophers. As a slight simplication of some of the following problems, we'll ignore maxims about code reuse, and choose to use separate procedures for each philsopher's strategy.
assert
statements to the code to test for a
race condition. If necessary, you may add other code
to keep track of information to use in these assertions.
proctype.
proctypes.
¶
One attempt is to only have a lock for only every other fork.
The idea is that each philosopher only needs to grab one lock.
printf.
They assert that the two forks are being held by that
philosopher.
_pid,
so that each process (philosopher) knows its number.
Since process numbers start with 0, not 1, you can either
use _pid+1, or renumber the
philosophers 0…2.
Then, replace the type="inline">mtype with
a simple numerical encoding.
To compute the appropriate right_fork
value, use the modulo operator (%).
In the following, consider an arbitrary number of philosophers. Three philosophers is only a special case.
The following is an example of the distinction between deadlock and livelock. Consider two people walking in a hallway towards each other. The hallway is wide enough for two people to pass. Of interest is what happens when the two people meet in the hall. When meeting on the same (left/right) side of the corridor, a polite strategy is to step to the other side of the hallway. A more belligerent strategy is to wait for the other person to move. With two polite people there is the possibility of livelock, while with two belligerent people there is the possibility of deadlock. (As an aside, note that one polite and one belligerent person together in a hall don't have any problems.)
A key to modeling the problem is to abstract away the unnecessary details. In particular, there is no need to model the people walking down the hall, as nothing interesting happens then. Similarly, the hallway can be modeled as being only wide enough for two people to pass. By these restrictions, we reduce the data to only four possibilities: each of the two people can be on either of two sides of the hall. Keeping the state space small allows SPIN to solve more problems, solve them faster, and report traces that are more succinct and, thus, easier to understand.
The following is one of the many ways to code this.
/* Code assumes these two values add to two. */
#define NUM_BELLIGERENT 2
#define NUM_POLITE 0
/* Indicates position of two people.
* Values: 0 or 1, for two sides of hall.
* 2 means unitilized.
*/
int position[2] = 2;
active [NUM_BELLIGERENT] proctype belligerent()
{
/* Non-deterministically set up starting position. */
if
:: true -> position[_pid] = 0;
:: true -> position[_pid] = 1;
fi;
/* Wait for other person to initialize. */
position[1 - _pid] != 2;
do
:: position[1 - _pid] == position[_pid] ->
/* Wait for other person to move. */
skip;
:: else ->
/* Success. */
break;
od;
}
active [NUM_POLITE] proctype polite()
{
/* Non-deterministically set up starting position. */
if
:: true -> position[_pid] = 0;
:: true -> position[_pid] = 1;
fi;
/* Wait for other person to initialize. */
position[1 - _pid] != 2;
do
:: position[1 - _pid] == position[_pid] ->
/* Move to other side. */
position[_pid] = 1 - position[_pid];
:: else ->
/* Success. */
break;
od;
}
The process scheduler in an operating system is a typical example of where we are concerned with fairness. If we have multiple processes running on a single processor, we break time into finitely-long intervals during which we execute one of the processes. We'd like to ensure that each process gets a ``fair'' share of the CPU time.
Your model should have an ``OS'' process (the scheduler) and multiple ``user'' processes, using this framework. The user processes are non-terminating — they always want to get the CPU's next time slice. I.e., the user processes will be continually enabled from the perspective of your own scheduler, although not from that of SPIN. But, we aren't modeling the user computation, those processes just print a message. Your scheduler, which is also non-terminating, somehow picks among the processes which will execute.
proctype,
inside its loop.
Why is this insufficient for SPIN to verify the weak fairness?