CS474 - Mutual Exclusion

Hardware Solutions

There are a number of mutual exclusion solutions that depend on the computer's hardware. If useable, these are really the best way to go, since they will be the most efficient you'll find. But they aren't always useable....

Software Solutions

Let's go through a series of algorithms that attempt to solve the synchronization problem. Any correct solution must meet the following criteria:
Mutual Exclusion
Only one process at a time may be in the critical section
Speeds
No assumptions can be made about the relative speed or the number of CPUs.
Progress
If no processes are in the critical section, and some processes want to enter the critical section, only those processes that want to enter can participate in the decision and the decision can't be postponed indefinitely
Bounded Waiting
There must be a bound on the number of times that other processes are allowed to enter the critical section while a particular process is waiting to enter
We'll simplify the problem a bit by assuming we have two processes, process 0 and process 1. After we've got a two-process solution, we'll give a multiple-process solution.

A Process Using Mutual Exclusion

We model a process as a loop, as follows:

while (1) {
    /* non critical section code */
    enter(); /* also called entry code */
    /* critical section code */
    leave(); /* also called exit code */
}

First try: see if the other guy has the lock

Assume lock is a shared variable

int lock;

void enter()
{
    while (lock == 1);
    lock = 1;
}

void leave()
{
    lock = 0;
}

Problem: Entry code is itself a critical section, and can fail. Doesn't provide mutual exclusion.

Second try: take turns

Assume turn is a shared variable, and ME is a local symbol that is defined to contain the number of the current process.

int turn;

void enter(int me)
{
    while (turn != me);
}

void leave(int me)
{
    turn = 1 - me;
}

Problem: Doesn't satisfy the progress requirement.

Third try: Keep track of whether the other guy wants in

Assume me as in the second try, and also assume a shared boolean array flag[2] which keeps track of whether flag[i] wants to enter its critical section

int flag[2];

void enter(int me)
{
    flag[me] = 1;
    while (flag[1-me]);
}

void leave(int me)
    flag[me] = 0;
}

Problem: Doesn't satisfy bounded waiting. As a matter of fact, both processes can get stuck in the entry code and wait there forever (sort of like Disney's ``Chip & Dale'').

Fourth and last try (Peterson's Algorithm): combine second and third

Make all the assumptions from both the second and third tries

int flag[2];
int turn;

void enter(int me)
{
    flag[me] = 1;
    turn = 1-me;
    while (flag[1-me] && (turn == (1-me)));
}

void leave(int me)
{
    flag[me] = 0;
}

This solution meets all of the requirements.

Multiple Processes

This algorithm is called the ``bakery algorithm,'' because it's based on the ``take a number'' box that bakeries use (actually, I've never seen one in a bakery. But the Motor Vehicle Division uses one).

Assume two shared arrays, each with one element per process that might be trying to enter the critical section: choosing[NUMPROCS] and number[NUMPROCS].

number[j]
This array is used to keep track of all the processes currently trying to enter the critical section. The first process to try to enter gets assigned the number 1, the second gets 2, the third gets 3, and so forth. When all the processes that tried to enter the critical section have left it again, we start assigning numbers again with 1.
choosing
While we're comparing the number we chose to the numbers everybody else chose so we can see if we can get in, we may encounter somebody who's still choosing their number. If we do, we wait until they're done. We use this array to announce whether we're currently choosing or not.

What strikes me as crucial to this code is that there are no write-write races. A process never modifies a variable owned by somebody else, though it looks at variables owned by others.

Entry Code

int choosing[NUMPROCS];
int number[NUMPROCS];

void enter(int me)
{
    choosing[me] = true;
    
    for (j = 0; j < NUMPROCS; j++)
        if (number[j] > number[me])
            number[me] = number[j];
    number[me]++;

    choosing[me] = false;

    for (j = 0; j < NUMPROCS; j++) {
        while (choosing[j]);
        while (number[j] &&
               ((number[j] < number[me]) ||
                ((number[j] == number[me]) && j < me)));
}


Exit code

void leave(int me)
{
    number[me] = 0;
}


Last modified: Fri Sep 9 11:12:34 MDT 2005