CMSC 330, Summer 2008

Organization of Programming Languages

Project 5

Due July 23, 2008
11:59:59pm

Introduction

In this project you are to write a concurrent Java program using threads in Java. Even if this problem is a simple one that can be solved without using threads, you are required to use threads for the tasks that will be explained.

Setting

The setting consists of a baker, a long narrow desk, several workers, sufficient bags, seals and pricing tools, and a box where ready-to-ship muffins are placed.

The number of muffins to be produced and the number of workers will be given as command line arguments to your program.

The baker only bakes muffins and puts them onto the desk. The workers can bag, seal, and price the muffins. After a worker prices a muffin, s/he puts it into the box where ready-to-ship muffins will be placed. We will call this task as "shipping" for simplicity. We will say "the worker shipped the muffin" instead of "the worker put the muffin into the box where ready-to-ship muffins are placed".

As it happens in real life, a muffin first should be baked, then bagged, then sealed, then priced and then shipped. It's not possible for a muffin to be bagged before baked, to be sealed before bagged, to be priced before sealed, or to be shipped before priced.

Since the baker and workers are intended to work concurrently, you would have a separate thread for each of the workers and the baker. The threads should be created once the command-line arguments are read and processed. Once they are created, the concurrent execution of these threads should start almost at the same time.

The tasks represent the threads processing data in certain ways, sometimes moving it from one data structure to another or one thread to another.

When a worker or the baker finishes, that thread finishes executing and when all threads finish executing, the program must terminate normally and return to the command-line prompt, that is, to the UNIX shell.

Input

Your program will receive 3 command-line arguments.

  • the number of muffins
  • the capacity of the desk
  • the number of workers

This is all the input that will be provided to your program. Your program won't read anything from the standard input.

Rules of the game

Names

All names are case-sensitive. The quotes should be left out when using the names.

The desk is named as "desk".

The baker is named as "Baker".

If we have 3 workers, the workers are named as "worker_0", "worker_1", and "worker_2". In general, if we have N workers, the workers are named as "worker_0", ..., "worker_N-1".

Similarly, if we have 3 muffins, they are called "muffin_0", "muffin_1", and "muffin_2". In general, if we have N muffins, the muffins are named as "muffin_0", ..., "muffin_N-1".

The Desk

The desk is a long narrow strip of wooden platform that the muffins are put on. The desk has two ends, one is the rear end, and the other is the front end. A muffin is always put to the rear end and is always taken from the front end. The desk is conceptually a FIFO (first-in-first-out) queue where the order of muffins are preserved.

The desk has a capacity in terms of number of muffins. The capacity will be given as a command line argument and will determine the maximum number of muffins that can be available on the desk at the same time.

Initially the desk is empty.

The Baker

The baker bakes muffins one at a time and puts the baked muffin to the desk every time. The baker doesn't bake a second muffin before he puts the already baked muffin in his hand to the desk. In other words, he only bakes the next muffin after he puts the current one onto the desk.

The baker will bake as many as muffins as the number of muffins given to your program as a command-line argument. After the baker bakes and puts all muffins to the desk, he finishes, and is free to go home.

Let's denote the command-line arguments as m, c, and w (m is the number of muffins, c is the capacity of the desk, and w is the number of workers).

If m < c+w, the baker can put muffins to the desk as long as the capacity of the desk has not been reached. If the capacity of the desk has been reached, then he has to wait. He waits until an opening on the desk becomes available, and then continues to put muffins.

If m >= c+w, then the baker should always leave 1 space as empty on the desk. In other words, the baker has to wait if the desk contains c-1 muffins, and can put muffins to the desk as long as there are 2 or more empty spaces on the desk.

The baker does nothing while he waits.

Workers

Every worker handles only one muffin at a time. Every worker has access to the desk and s/he can put a muffin onto the desk and get a muffin from the desk. Since a worker cannot handle more than one muffin at a time, s/he has to put the muffin in hand before taking another one from the desk.

Every worker does one task at a time, be it bagging, sealing, pricing, and shipping. More precisely, this means that a worker will get a muffin from the desk, either bag it, seal it, or price it, and then will put the muffin back to the desk. The only case where a muffin is not put back onto the desk is when a muffin is shipped. As soon as a muffin is shipped, its processing is complete.

As soon as a worker ships a muffin, s/he can continue his/her job and get another muffin from the desk.

Since tasks depend on each other, it's determinate for a worker what to do with a muffin as soon as s/he gets it from the desk. If, for example, the muffin the worker gets has already been bagged and sealed, s/he will price it and put it back onto the desk.

When a worker wants to put a muffin back to the desk, s/he has to wait until there becomes an opening again if the desk has reached its capacity. Also, if a worker wants to get a muffin but there are no muffins on the desk, s/he has to wait until there becomes at least one muffin available on the desk. While waiting, a worker does nothing.

A worker is finished when all muffins are shipped.

Tasks

You will be provided a sample Task.class file. (It's called sample because the definitions of the methods inside may be altered. Since you only need to be concerned about how to use them, you don't need to worry about the possible alterations of the definitions internally.) It will provide you with the static methods that you will call.

The methods in the Task class represent the actual processing which the workers and the baker do on muffins. For example, you'll make a call to the "bag" method as Task.bag(n) to represent the task of bagging muffin_n.

You need to make the calls to bag(), seal(), and price() after the worker gets the muffin from the desk and before the worker puts it back to the desk. You need to make the call to ship() after the worker gets the muffin and before the worker gets another one from the desk.

You need to call the following:

  • bake(n) to make the baker bake a muffin_n.
  • putMuffin(n) to put muffin_n to the desk.
    You need to make the call before adding the muffin to your queue.
  • getMuffin(n) to get muffin_n from the desk.
    You need to make the call after removing the muffin from your queue.
  • bag(n) to make the worker bag muffin_n.
  • seal(n) to make the worker seal muffin_n.
  • price(n) to make the worker price muffin_n.
  • ship(n) to make the worker ship muffin_n.

Note: You need to handle the InterruptedExceptions these methods throw.

Output

All output should be done to the standard output and all printing should be synchronized. (More about this in the next section.)

You should print immediately after you make each call. In place of n's and k's, you need to print the appropriate numbers so that they are the names for the muffin and the worker, respectively.

  • bake(n) "Baker baked muffin_n"
  • putMuffin(n) According to the person who puts the muffin, you should print either of the following:
    "Baker put muffin_n to desk"
    "worker_k put muffin_n to desk"
  • getMuffin(n) "worker_k got muffin_n from desk"
  • bag(n) "worker_k bagged muffin_n"
  • seal(n) "worker_k sealed muffin_n"
  • price(n) "worker_k priced muffin_n"
  • ship(n) "worker_k shipped muffin_n"
  • when the baker finishes "Baker finished"
  • when worker_n finishes "worker_n finished"

Synchronization

Synchronization will be necessary to avoid corrupting data structures in your program and losing data.

You need to synchronize the access to the desk, for example. You also need to synchronize the printing to the standard output. Or, the muffins may become duplicated and printing may be interleaved. Synchronization is necessary where more than one thread attempts to do the same thing (or similar thing). You should not use synchronization more than necessary which will restrict your program's concurrent execution.

Error Conditions

If your program is given more than 3 command-line arguments, your program should consider the first three and ignore the rest.

All 3 command-line arguments should be positive integers parseable by java.lang.Integer parsing methods.

If your program is given less than 3 command-line arguments, or your program is given 3 arguments but at least one of them is not a positive integer as defined in the previous paragraph, then your program should print the following:

Usage:   muffins  capacity  workers

Your program should also print the message above if the capacity is given as 1. (The capacity should be given as 2, at least.)

Note: There are 3 space characters between "Usage:" and "muffins". And, there are 2 space characters before and after "capacity".

Example

This small example assumes that the class which begins execution is named Main, which is the required name and spelling for the class which must begin execution for your submitted project. The primary input to be provided shortly will comprise a larger (although intentionally non--exhaustive) example of valid input and correct results for it. Due to the number of lines, the output continues onto the next page.

Note that the standard output shown below is only one of the possible results your program may produce when run on this input!

> java Main 3 2 2 > test1
> cat test1
Baker baked muffin_0
Baker put muffin_0 to desk
Baker baked muffin_1
worker_0 got muffin_0 from desk
Baker put muffin_1 to desk
Baker baked muffin_2
Baker put muffin_2 to desk
Baker finished
worker_1 got muffin_1 from desk
worker_1 bagged muffin_1
worker_1 put muffin_1 to desk
worker_1 got muffin_2 from desk
worker_0 bagged muffin_0
worker_0 put muffin_0 to desk
worker_0 got muffin_1 from desk
worker_0 sealed muffin_1
worker_0 put muffin_1 to desk
worker_0 got muffin_0 from desk
worker_1 bagged muffin_2
worker_1 put muffin_2 to desk
worker_1 got muffin_1 from desk
worker_0 sealed muffin_0
worker_0 put muffin_0 to desk
worker_1 priced muffin_1
worker_0 got muffin_2 from desk
worker_1 put muffin_1 to desk
worker_1 got muffin_0 from desk
worker_1 priced muffin_0
worker_1 put muffin_0 to desk
worker_0 sealed muffin_2
worker_1 got muffin_1 from desk
worker_1 shipped muffin_1
worker_0 put muffin_2 to desk
worker_0 got muffin_0 from desk
worker_0 shipped muffin_0
worker_1 got muffin_2 from desk
worker_1 priced muffin_2
worker_1 put muffin_2 to desk
worker_1 got muffin_2 from desk
worker_1 shipped muffin_2
worker_1 finished
worker_0 finished

Checking your results

Each time you run your program the inherent uncertainty of the execution order of threads may cause different results, and your output may differ from the primary output. You may wonder how you can check whether your output is correct. We may check that whether you have followed the various requirements mentioned in the next section in various ways, by examining the your output or by examining your source code if necessary. We may check properties of your output to insure that your program is implemented properly, such as verifying that muffins are taken in the order they put, that every muffin that is not shipped yet is returned back to the desk. We could also check whether any muffin is lost, duplicated, or confused with another one, whether the task order is violated, and other things. Since incorrect concurrent programs often give different results different times they are run, sometimes working correctly and sometimes not, the instructional staff may choose to run your program several times in order to determine what grade to give your project. If your program does produce variable results, we reserve the right to give you the average grade produced by these multiple runs of your program.

Implementation Requirements

Please carefully note the following requirements:

  • Any call to any sleep() method is PROHIBITED!
    If usage of any such method is detected, your program may be counted as not working on the primary input.
  • You may be able to implement this project quite easily by writing a sequential program not using threads, but unless you use threads as described above you will receive NO CREDIT for the project, and consequently you may not pass the course.
  • Your program must use calls to methods in Task as explained in the previous sections, or your project will be counted as not working on the primary input.
  • You must implement the synchronization described above so all output is always in adjacent lines, or your project will be counted as not working on the primary input. (There should not be any blank lines and every output string given above must be on a separate line (as you see in the example given in the Example section), that is, every output string must be followed by newline.)
  • Your program must not introduce any unnecessary synchronization (for instance, preventing workers or bakers to execute concurrently when there is no necessity to do so).
  • Your program must halt of its own accord when all threads have finished.
  • If your project submission does not compile or it does not attempt to use threads, concurrency, and synchronzation, it may receive a 0 grade.
  • All threads (workers, the baker and the main thread) should be of equal priority. It happens so as default, that is if you don't set any priorities.

Project requirements

Each of your classes must be written in a separate source file, named with the name of the class. For example, a class MyClass must be in a source file named MyClass.java. Your program must compile successfully using the version of the Java compiler javac, and run successfully using the default version of Java installed on the Grace UNIX class cluster. Your program must compile correctly when the single UNIX command javac Main.java is executed. Your program must start from a class named Main, which must have a member function which is named public static void main(String args[]), i.e., your program must run correctly when the single UNIX command java Main is executed. Your program should write all of its output to its standard output.

Each source file in your program must have a comment near the top which contains your name, login ID, student ID number, and your section number. Note that although you are not being graded on your source code or style, if we cannot read or understand your code, we will not be able to help you should you have to come to office hours, until you return with a well--written and clear program.

The Campus Senate has adopted a policy asking students to include the following statement on each assignment in every course: "I pledge on my honor that I have not given or received any unauthorized assistance on this assignment." Consequently, your program is requested to contain this pledge in a comment near the top of your Main.java source file. See the last section for other extremely important information regarding violations of academic integrity.

Submitting your project

To submit, gtar all of your source files using the UNIX gtar command with the compression option 'z', as in:

$ gtar -zcvf proj5.tgz *.java

This command assumes all .java files in the current directory are part of your project. Do not include any other files besides the Java source (.java) files necessary to run your program in your tgz-file; modify the command as appropriate if other Java source files which are not part of your project are in your directory. Also, do not include any .class files (such as the Task.class file or any other .class file) or any data files in your tgz-file!

Turn in your tgz-file using the submit server. You must test your tgz-file before submitting it! Submitting a corrupted tgz-file, or a tgz-file which doesn't contain all of your Java source files, will cause you to receive very little or no credit for this assignment.

Only the project which you electronically submit, according to the procedures provided, can be graded; it is your responsibility to test your program and verify that it works properly before submitting. All of the outputs must exactly match the correct answers. The grading for this project has been decided to be done differently than the one stated in the syllabus. (Hardcoding is considered as fabrication. Students doing this will get 0 and be reported to the Honor Council.)

Do not delay in submitting your program; it is recommended you submit a minimally--working version as soon as you have one, and as you continue to test and refine your project just resubmit every improved version you develop.

Academic Integrity

The Campus Senate has adopted a policy asking students to include the following statement on each assignment in every course: "I pledge on my honor that I have not given or received any unauthorized assistance on this assignment." Consequently your program is requested to contain this pledge in a comment near the top.

Please carefully read the academic honesty section of the course syllabus. Any evidence of impermissible cooperation on projects, use of disallowed materials or resources, or unauthorized use of computer accounts, will be submitted to the Student Honor Council, which could result in an XF for the course, or suspension or expulsion from the University. Be sure you understand what you are and what you are not permitted to do in regards to academic integrity when it comes to project assignments. These policies apply to all students, and the Student Honor Council does not consider lack of knowledge of the policies to be a defense for violating them. Full information is found in the course syllabus; please review it at this time.

Valid HTML 4.01!