Return to the lecture notes index

Lecture 9: Friday, September 17, 2001

Slides Used During Today's Lecture

Monitors, Philosophers, and Deadlock introduction(ppt)

Reading:

Chapter 8

A Monitor Question From Last Year's Midterm

In grading the homework, it appeared that more people than I liked were having difficulty with monitors. This isn't surprising since the monitors question on last year's midterm exam beat up more people than we would have liked. Since I'd like to avoid that situation again this year, I thought I'd take the early-warning that the homework provided and do another example.

Let's take a look at last year's monitor question and produce solutions using all three flavors of monitors. Then, if you'd like, you can resubmit problem #5 of the homework -- I'll need your new submission by 4:30 on Monday so that I have time to grade it and hand it back.

3. [15 points] Fire? What fire?

Fire! The 15-412 students drop their exams and rush to evacuate via the only exterior doorway. Meanwhile, the valiant civil servants of the Pittsburgh Fire Bureau rush toward the same doorway to douse the fiery inferno. But alas, the celebrating students can't exit through the doorway at the same time the firefighters enter. Unless the situation is controlled, the firefighters will enjoy a picnic lunch outdoors and the students will become toast.

You must model a solution to this terrible plight that allows the students to escape and enjoy their weekend and allows the firefighters to enter the building and prevent any more singed concrete.

Below are the rules that allow maximum use of the doorway, without deadlock:

Furthermore, your solution must ensure the following: Your solution should use a single monitor as the tool for guaranteeing mutual exclusion and synchronization. Please state which semantics are required by the monitor used in your solution: Hoare, Mesa, or Brinch Hansen. Be sure to declare an initialize all shared variables.

Your solution should have a single monitor with the following entry procedures:

A Mesa Semantics Solution

In order to solve this problem, we must begin by asking ourselves the question, "What is shared?" The answer to this question is the doorway -- out monitor must control access to the doorway.

What is the sharing discipline? It is not mututal exclusion. It is two firefighters or one student, such that firefighters can starve students, but not vice versa.

To do this we can envision a system where students and firefighters get in line in front of the doorway. If the doorway is empty, the arriving person goes right into it. Otherwise he/she gets into the right line. Upon exiting the doorway, the person signals a person or two people to enter, as appropriate.

MESA MONITOR doorway
{
   int firefighterInDoorway = 0;     
   int firefighterWaiting = 0;
   int studentInDoorway = 0; 

   condition student, firefighter;

   Entry FirefighterEnterDoorway()
   {
     fireFighterWaiting++;
     
     while ( (studentInDoorway) || (firefighterInDoorway >= 2) ) 
         firefighter.wait();
       
      firefighterInDoorway++;
      firefighterWaiting--;
   }


   Entry FirefighterExitDoorway()
   {
     firefighterInDoorway--;
 
     if (firefighterWaiting())
         firefighter.signal();
     else
        if (!firefighterInDoorway)
             student.signal();      
   }


   Entry StudentEnterDoorway()
   {
        while ( (studentInDoorway) || (firefighterInDoorway) 
                (firefighterWaiting) )
             student.wait();

        studentInDoorway++;
   }


   Entry StudentExitDoorway()
   {
       studentInDoorway--;

       if (firefighterWaiting)
       {
          firefighter.signal();
          firefighter.signal();    
       }
       else
         student.signal();
   }
}
A Hoare Semantics Solution

In order to convert the Mesa semantics solution into something that functions with Hoare semantics, we must look at two things:

  1. We don't need to loop around the predicates (conditions) for which we are waiting -- no one can run between the time that we are signalled and the time that we run. The whiles can become ifs.
  2. We need to check the code after ever time we signal and ask ourselves if it is still safe -- we need to remember that we could be preempted before it runs. Are all of the resources still available? In the state that we expect?
In this case, we can change the while's to ifs (whiles are still safe), and the code after signals is safe. The only time we do something after signalling is when we signal twice in a row. In that case the number of firefighters in the doorway could have changed from 0 to 1. But even if it is now 1, we're still safe -- it could not have gone to 2.
HOARE MONITOR doorway
{
   int firefighterInDoorway = 0;     
   int firefighterWaiting = 0;
   int studentInDoorway = 0; 

   condition student, firefighter;

   Entry FirefighterEnterDoorway()
   {
     firefighterWaiting++;

     if ( (studentInDoorway) || (firefighterInDoorway >= 2) ) 
         firefighter.wait();
       
      firefighterInDoorway++;
      firefighterWaiting--;
   }


   Entry FirefighterExitDoorway()
   {
     firefighterInDoorway--;
 
     if (firefighterWaiting())
         firefighter.signal();
     else
        if (!firefighterInDoorway)
             student.signal();      
   }


   Entry StudentEnterDoorway()
   {
        if ( (studentInDoorway) || (firefighterInDoorway) 
                (firefighterWaiting) )
             student.wait();

        studentInDoorway++;
   }


   Entry StudentExitDoorway()
   {
       studentInDoorway--;

       if (firefighterWaiting)
       {
          firefighter.signal();
          firefighter.signal();    
       }
       else
         student.signal();
   }
}
A Brinch Hanson Semantics Solution

When we convert from Hoare sematnics to BH semantics, we have to convert ever signal() operation to a signalAndExit() operation. This means that any code after a signal has to be moved somewhere else.

In the case of the previous solution, this affects us in one critical place -- we can no longer signal twice in a row in order to let two firefighters into the doorway. Instead we convert the solution so that we signal the first firefighter. Firefighters must become more considerate -- if a firefighter enters the doorway and there is room for another, she/he signals another firefighter to enter. Of course, we need to be careful that this signal is the last thing that the firefighter does within the monitor -- as before, the operation is signalAndExit().

BH MONITOR doorway
{
   int firefighterInDoorway = 0;     
   int firefighterWaiting = 0;
   int studentInDoorway = 0; 

   condition student, firefighter;

   Entry FirefighterEnterDoorway()
   {
     firefighterWaiting++;

     if ( (studentInDoorway) || (firefighterInDoorway >= 2) ) 
         firefighter.wait();
       
     firefighterInDoorway++;
     firefighterWaiting--;

     if ((firefighterInDoorway < 2) && (firefighterWaiting) )
          firefighter.signalAndExit();
   }


   Entry FirefighterExitDoorway()
   {
     firefighterInDoorway--;
 
     if (firefighterWaiting())
         firefighter.signal();
     else
        if (!firefighterInDoorway)
             student.signalAndExit();      
   }


   Entry StudentEnterDoorway()
   {
        if ( (studentInDoorway) || (firefighterInDoorway) 
                (firefighterWaiting) )
             student.wait();

        studentInDoorway++;
   }


   Entry StudentExitDoorway()
   {
       studentInDoorway--;

       if (firefighterWaiting)
       {
          firefighter.signalAndExit(); 
       }
       else
         student.signalAndExit();
   }
}

Dining Philosophers

The problem:

Rules
  • Philosophers alternate between thinking and eating
  • 2 chopsticks are required to eat
  • Philsophers are dignified and never grab with both hands -- they pick up the chopsticks one at a time
  • A philospher is too polite to steal a chopstick from a colleague
  • The philosophers cannot be allowed to starve
  • More than one philosopher must be able to eat at a time.

Approach #1

  #define left(i) (i)
  #define right(i) ((i-1) % 5)
  MONITOR fork {
     int avail[5] = {2, 2, 2, 2, 2 }; /* forks available to each phil */
     condition hungry[5];

     entry pickup_fork (int phil) {
         if (avail[i] != 2) hungry[i].wait();
         avail[left(I)]--; avail[right(I)]--;
      }
    entry putdown_fork (int phil)  {
         avail[left(i)]++: avail[right(i)]++;
         if (avail[left(i)] == 2) hungry[left(i).signal();
         if (avail[right(i)] == 2) hungry[right(i)].signal();
    }
  }
  

Starvation: Why?

This approach fails due to a very common concern -- starvation. There is no guarantee that an individual philospher would ever be able to eat.

In general, starvation occurs because different parties are waiting for different conditions with no common way to select among them. The solution is very straight-forward. All parties should wait for the same condition. An easy fix might be a common queue for waiting processes.

Another Approach

This time let's look at a sempahore-based approach (I like to mix it up so that we don't play favorites).

  Semaphore chopstick[5] = { 1, 1, 1, 1, 1 };

  while (1)
  {
     P(chopstick[i]);
     P(chopstick[(i+1) % 5]);
     <<< eat >>>
     V(chopstick[i]);
     V(chopstick[(i +1) % 5]);
     <<< think >>>
  }
  

But this solution isn't right either! What can happen? It is possible that the philosophers won't cooperate resulting in no food for anyone -- consider what would happen if each philosopher grabbed his/her left chopstick. No one would be able to eat, because everyone would be waiting for someone else to put down his/her chopstick first. This is called deadlock.

What is Deadlock?

First, let's define a resource:

Now, let's formally define deadlock:

Why Does Deadlock Occur?

Deadlock occurs if these conditions are satisfied:

Simple Defense: Serialization

This attacks the circular wait condition.

Other Defenses

It is possible to attack the other three conditions as well -- but this is often trickier:

Dining Philosophers -- Serialized

If we enumerate the chopsticks and for each philosopher to request the lowest number chopstick first, deadlock cannot happen. This is the same as each philosopher picking up the chopstick on the left -- except for P4. She/he will pick up the right chopstick first, since it has a lower number -- this breaks circular wait. Notice that there isn't a cycle of dependency.

  Semaphore chopstick[5] = { 1, 1, 1, 1, 1 };

  while (1)
  {
     if (i < ((i+1) % 5)) {
          P(chopstick[i]);
          P(chopstick[(i+1) % 5]);
     } else {
         P(chopstick[(i+1) % 5]);
         P (chopstick[i]);
     }
     <<< eat >>>
     V(chopstick[i]);
     V(chopstick[(i +1) % 5]);
     <<< think >>>
  }