UNIVERSITY OF WISCONSIN-MADISON
Computer Sciences Department
CS 537
Spring 2000
A. Arpaci-Dusseau
Sample Problems #2

Problem 3: Dining Philosophers

A classic synchronization problem, presented by Dijkstra in 1965, is the Dining Philosophers Problem. In this problem, there are 5 philosophers sitting at a round table. Philosophers repeat (forever) thinking and eating. There is a single chopstick shared between each pair of philosophers. As you know, a philosopher must have two (2) chopsticks to be able to eat!

Below is code that attempts to solve this problem. The philosopher constructor is called five times in the main method (each time with a parameter 0 to 4 indicating the id of the philosopher) and then 5 philosopher threads are created and started. Think() and Eat() are methods provided to you (already written). You don't know how long each of these takes to complete.

Unfortunately, this code makes a timing assumption about how it is used, can have deadlock even when used "correctly", and does not initialize the value of the semaphores.

For this problem you need to do three things:

  1. Determine the assumption that the Philosopher class makes about the order in which operations in main() are performed. Show code in main() for constructing the Philosopher objects and starting the threads that works correctly and code that depends upon the behavior of the scheduler to work correctly.
  2. Without adding any state variables, fix this code so that it cannot deadlock. You do not need to make the code as efficient as possible, just ensure that deadlock cannot occur.
  3. Specify the correct initial values for the semaphores.

The following code might not work correctly if the scheduler decides to start up and schedule a philosopher thread before the semaphore associated with another philosopher has been created and initialized.

for (i = 0; i < philCount; i++) {
    p[i] = new Philosopher(i);
    t[i] = new Thread(p[i]);
    t[i].start();
}    

Code that works correctly no matter how scheduler decides to run the philosopher threads:

for (i = 0; i < philCount; i++) {
    p[i] = new Philosopher(i);
    t[i] = new Thread(p[i]);
}    

for (i = 0; i < philCount; i++) {
    t[i].start();
}    
class Philosopher implements Runnable{

    private static Semaphore chopstick[5];
    private int p;

    Philosopher (int id) {
        p = id; 
        chopstick[id] = new Semaphore(1);       
    }

    public void run() {
        while (1) {
            Think();
            if (p!=0) {
                fork[p].P();
                fork[(p + 1) % 5].P();
            } else {
                chopstick[(p + 1) % 5].P();
                chopstick[p].P();
            }
            Eat();
            chopstick[p].V();
            chopstick[(p + 1) % 5].V();
        }
    }
}

Problem 4: Implementing Semaphores with Monitors

You are to write the code for a class that simulates a general semaphore; you are to use Java with monitors (synchronized methods) as your synchronization mechanism. Your solution will include a constructor, P(), and V() methods.

Below is the skeleton of a solution and you should fill in the details. Make whatever additions or changes you feel necessary.


class Semaphore
{
    private int count;



    public Semaphore (int i)
    {
        count = i;


    public synchronized void P()
    {
        while (count == 0) {
            try { wait(); }
            catch (InterruptedException e) { }
        }
        count--;


    public synchronized void V()
    {
        count++;
        notify();

    }
}

Problem 5: Writing Semaphore Code

You are in charge of filling buses with passengers at a bus terminal.

Busses and both types of passengers arrive at random. As buses arrive, your are to fill them up with passengers. Once a bus is full (containing 6 non-wheelchair, and 4 wheelchair passengers), it is allowed to leave the terminal, along with its passengers.

You are responsible only for loading the passengers on the bus and having the bus depart. You don't need to worry about what happens to the busses or passengers after they leave. Each bus thread has available to it the methods ArriveAtTerminal(), OpenDoors(), CloseDoors(), and DepartTerminal(). You don't know how long each of these takes to complete.

Each passenger thread has available to it the methods ArriveAtTerminal() and GetOnBus(). After a passenger thread is safely on the bus, or a bus thread has left the terminal, that thread may terminate (exit).

Your assignment is to write the code that coordinates the buses and the passengers. Write your solution using semaphores as the synchronization mechanism. The syntax will be Java (but don't use synchronized methods!), extended with the Semaphore class as shown in the previous problem. Assume that each bus is a thread, and that the passenger is a thread. You are to define the semaphores that are to be used and how they are initialized. You shouldn't need to define any other shared state variables. You are to then write the code that the two different types of passengers will use, and the code for the buses. Create whatever other methods that you require.

Assume that threads are randomly created (by some code that we provide) and call either the Bus(), WheelPassenger(), or NonWheelPassenger() method. The BusStation() constructor will be called only once, at the start of the program.

class BusStation {

    // Declare Semaphores here 
    static Semaphore let_wheel_in;
    static Semaphore let_nonwheel_in;
    static Semaphore handshake;
    static Semaphore bus_lock;

    public BusStation() { 
    // Initialize semaphores here
        let_wheel_in     = new Semaphore(0);
        let_nonwheel_in  = new Semaphore(0);
        handshake        = new Semaphore(0);
        bus_lock         = new Semaphore(1);
    }
    public static void Bus() {
        ArriveAtTerminal();
        // Call OpenDoors(), CloseDoors() with other code as appropriate
        bus_lock.P();
        OpenDoors();
        for (int i = 0; i<4; i++)
        {
            let_wheel_in.V();
            handshake.P();
        }
        for (i = 0; i< 6; i++)
        {
            let_nonwheel_in.V();
            handshake.P();
        }
        bus_lock.V();
        CloseDoors();
        DepartTerminal();
    }
    void public static WheelPassenger() {
        ArriveAtTerminal();
        // Call GetOnBus as appropriate
        let_wheel_in.P();
        GetOnBus();
        handshake.V();
    }
    void public static NonWheelPassenger() {
        ArriveAtTerminal();
        // Call GetOnBus() as appropriate
        let_nonwheel_in.P();
        handshake.V();
    }
}