CS 537 Spring 2007
Assignment 2
Making Beer

Due Wednesday, Feb 28 at 1:00 am.
Note: There is a Frequently Asked Questions (FAQ) page for this project. It should be considered an integral part of the specifications. Check it from time to time, since new items may be added.

Errata

Feb 10
There are seven Java source files provided. An earlier version of these instructions only mentioned five of them and only six were installed in ~cs537-1/public/html/source/p2.

Introduction

For this assignment, you will write a trading system to supply Brewers with the four most common grains used in production of beer: corn, barley, rice, and wheat. The program will manage a single supplier and any number of brewers, as well as four traders who are responsible for stocking and delivering grain to brewers that request it.

Each trader maintains a stock of all four types of grain, but is a “specialist” for only one of them, called its specialty. From time to time, the supplier delivers a shipment of grain to the trader that specializes in that type of grain. For example, the supplier might deliver 30 bushels of barley to the BARLEY trader, and later it might deliver 20 bushels of wheat to the WHEAT trader. Brewers periodically place purchase orders with Traders. Each order specifies a number of bushels of each type of grain. An order can be placed with any trader; in this program Brewers choose Traders at random. A Trader will fill the order from its own stock if possible. If the specialist for grain G cannot fill an order because it does not have enough G on hand, it just waits until it gets more from the Supplier. If it is short of some grain that is not its specialty, it attempts to get more by trading with the specialist for that grain. In any case, the trader does not respond to the brewer until it can fill its entire order. To keep things simple, we will assume, somewhat unrealistically, that bushels of corn, barley, wheat, and rice are all equally valuable. That is, three bushels of corn can be swapped for three bushels or wheat, three bushels of barley, etc.

For example, suppose a brewer places an order with the WHEAT trader for for 4 bushels of wheat and 1 bushel of barley.

A trader treats a request from another trader just like an order from a brewer. If it can fill it immediately, it will. Otherwise, since the request must be for its specialty grain, it will just wait until it gets more from the supplier.

Implementing the System in Java

We have supplied four classes, P2, Supplier, Brewer, and Order; one enumeration, Grain; and and one interface, Trader. The classes Supplier and Brewer are meant to be used as threads (that is, they implement Runnable). You are expected to write a class TraderImpl that implements Trader. TraderImpl is a “monitor” class, meaning that a Trader does not act on its own, only in response to a call from a Supplier or Brewer. P2 is the main class. It creates one Supplier thread and some number (specified on the command line) of Brewer threads and starts them running.

From time to time the Supplier thread delivers a load of some grain to the specialist for that grain by calling the Trader method


    public void deliver(int bushels);
From time to time a Brewer places an order by contacting a random Trader and calling the Trader method

    public void get(Order order);
The order order contains one non-negative integer for each type of grain, indicating how much of that grain is desired. For example, to get three bushels of wheat and one bushel of barley from the BARLEY Trader, a Brewer might use code like this:

    Order order = new Order();
    order.set(Grain.WHEAT, 3);
    order.set(Grain.BARLEY, 1);
    P2.specialist(Grain.BARLEY).get(order);
As you can see, the class P2 provides several handy utilities as static methods. We will explain these in more detail below.

A Trader who does not have enough of a grain on hand to satisfy a request may try to get more from the specialist in that grain by calling the method


    public void swap(Grain what, int amt);
This method indicates that the calling Trader is willing to swap amt bushels of grain what for amt bushels of the supplier's specialty. For example, the call

    P2 . specialist(Grain.WHEAT) . swap(CORN, 2);
indicates that the caller is willing to swap 2 bushels of corn for two bushels of wheat. After the call returns (calls to get and swap never fail, but they may delay the caller for a while), the caller should subtract 2 from its supply of corn and add 2 to its supply of wheat.

When a Trader cannot satisfy a get or swap request because it does not have enough of its specialty grain, it blocks the caller (using the Java wait method) until it can. A Trader that is blocked waiting for something should continue to accept calls from the Suplier, Brewers, or other Traders.

Your solution must be deadlock-free, but it does not have to be "fair". In particular, you do not have to prevent starvation (an unlucky brewer who repeatedly gets passed over).

Details

Grab copies of all seven .java files form ~cs537-1/public/html/source/p2:

    mkdir project2
    cd project2
    cp ~cs537-1/public/html/source/p2/*.java .
Read the documenatation online.

WARNING: These classes may need to be updated from time to time. Check the Frequently Asked Questions (FAQ) frequently to see if you need to fetch a fresh copy.

GetOpt.java (documentation)

This is a handy class for parsing command-line options. It is used by Project2.main() so you need copies of it, but you do not need to read or understand it (but you can if you like).

P2.java (documentation)

This is the main program. It accepts two command line arguments: the number of brewer and the number of iterations executed by the Supplier before it terminates. There are also two optional command-line flags
-v
Request verbose debugging (see setVerbose).
-r
Use the same random seed each time the program is run. This will cut down the amount of variability from run to run and may be helpful in reproducing errors.

In addition to the main method, P2 provides several public static methods.

See the online documentation for more details.

Supplier.java (documentation)

This Runnable class has a constructor and methods run and getProduction. The run method goes through a number of iterations specified in the constructor. On each iteration, it produces a random amount of a random grain and delivers it to the appropriate Trader by calling its deliver method. The main program creates a Supplier thread, starts it running, and waits for it to finish (by returning from run()). When the Supplier is done, the main program waits three seconds, kills off the Brewer threads by interrupting them, and prints some statistics. Each Brewer thread will see the interrupt by getting an InterruptedException. The method Brewer.run() catches this exception, prints a message, and returns.

Your code should not catch InterruptedException. Any method that contains a call to a method that throws InterruptedException should simply declare that it throws InterruptedException in its header.

The getProduction() method is used by P2.main to audit the global state when the system shuts down.

Brewer.java (documentation)

This class is also a simple Runnable. It iterates generating random orders and calling the get method of random Trader until it is interrupted by the main program. It also has a getConsumption method used by P2.main to audit the global state when the system shuts down.

Trader.java (documentation)

This is an interface. You must write a class called TraderImpl that implements this interface. (The name of your class must be spelled exactly this way.) Your class must implement the four methods specified in the interface, as well as a constructor that has one argument of type Grain. All four methods should be protected against race conditions, but you will probably find that they cannot all be synchronized methods.

Grain.java (documentation)

Order.java (documentation)

Simple data structures used to support the methods described above.

Hints

Remember the cardinal rule of concurrent programming:
Any variable that may be accessed by more than one process and modified by any of them should only be accessed inside a critical section.
In the context of this project, that means that fields of class TraderImpl should only be accessed inside synchronized methods. On the other hand, there's another rule that will help you avoid deadlocks:
A synchronized method should never call a synchronized method of another object.
There are exceptions to this rule, but they are rare. In the context of this project, it means that TraderImpl.get cannot be synchronized because it needs to call TraderImpl.swap in another Trader. Instead, define your own private synchronized methods to read and/or update fields of TraderImpl and call them from TraderImpl.get.

There will undoubtedly be more hints provided by your fellow students. Watch the Frequently Asked Questions (FAQ) for more information.

What to turn in

Turn all .java files you wrote or modified. Do not turn in .class files. No transcript is necessary.

Grading

Please carefully consider all the grading criteria discussed in class. Correctness will be 80% of your grade, and style will be 20% of your grade.

1 However, at first you should probably look only at the first 3 questions in the FAQ. The remaining questions involve "advanced" topics that will only confuse you if you're trying to get the basic functionality working.


Last modified: Sat Feb 10 17:33:45 CST 2007 by Marvin Solomon