CMPSCI 377: Operating Systems
Lab 2: Threads and Synchronization
Due: October 23, 2002 23:59
October 22: We talked in class today about the confusion between
the row/col and the X/Y coordinate systems. The robot and goal positions
are specified in row/column format. In the code that we have supplied,
the implicit assumption
that we made is that X==row and Y==col. Yes, it is confusing.
Rather than make changes to the base code at this late hour (as I
suggested that I might do in class), I think it is best that we do not do
this at the risk of introducing other bugs. Please adjust your code
accordingly. Sorry for the confusion.
Purpose of Assignment
This assignment will give you experience in the creation and management of
multiple, interacting threads.
This lab project will count for 30% of
your lab grade (so 11.7% of your final grade).
- Program Design (including data structures): 25%
- Program Implementation: 25%
- Correctness: 30%
- Documentation: 20%
You may work with one other person in the class. If
you choose to do so, please send email to the professor and the TAs
in order to indicate who your partner will be and which of the two
of you will be handing in the program. Do not share/accept
code with/from anyone else or the net.
Where to do your work
For this lab, place your final submission in the following directory:
If your files are not located in the right place or our robot cannot read
your homework at the time of submission, then we will not be able to
grade it. In order to make sure that our robot can pick up your program,
please check things by executing the
script with the
You need only hand-in one copy of your program.
For this lab, we are solving a problem involving multiple,
coordinated robots. As you have already discovered in lab 1,
an input file specifies a map, some set of robots of
various colors (red, green, and blue), and some set of goals (red,
green, blue, and black). The robots and goals "live" on a
discrete grid of a specific size (think graph paper). Some grid squares
are occupied by obstacles. The task is for the individual
robots to move around on the grid and "eat" goals without running into
each other or any obstacles.
Your program must:
- As in lab 1, your program must parse the map file and place the
information into an appropriate data structure (one is provided).
Your program should detect
and trap any errors that might exist in the file and display an
appropriate error message. The file format is identical to that of
- Create a thread that will control the movements of each robot (if
there are N robots, then there will be N threads). This thread must
run at NORM_PRIORITY. Be careful that the individual robot threads do
not make conflicting updates to the common data structures.
- Create a thread that will update the display at regular intervals.
This thread must run at NORM_PRIORITY+1 and execute at approximately
30 millisecond intervals.
- Create a thread that will write the position of each robot to
a file ("log.txt") at regular (100 ms) intervals. Be careful to ensure
that only valid data is written to the file. See the example
map1.log for the
map1.txt file. See below for more details
of the format of the log file.
- Once all goals are consumed, the program must detect this and prepare
the program to be shut down.
- In order to ensure that the different threads do not interfere with
one-another, you must identify their critical sections and use the
appropriate synchronization primitives in your code (see below for
a few more hints on this).
The rules for the world physics are as follows:
- Robots may only eat goals that are either black or their own color.
- Once a goal is eaten, it is removed from the display and cannot
be eaten again. This is indicated in the data structure
by setting isEaten to true (see below).
- At each step, a robot may only move one square in the up/down/left/right
- Robots may not move to a grid square that is occupied by an obstacle
or by another robot.
- Robots may occupy the same grid square of a non-matching goal. However,
the goal is not "eaten", and will remain in place after the robot leaves the
- When a robot no longer has edible goals to consume, it dies and
stops moving. This is indicated in the data structure by setting
bDead to true (see below).
What we provide:
- A predefined, public data structure that describes the grid and
and the locations of the robots, obstacles and goals.
This is defined in
- A predefined, public data structure that describes a goal.
This is defined in
- A predefined data structure that extends the thread class
and defines the Abstract_Robot
This class must be extended
by you to implement the movement of the robots and to handle all of the
necessary bookkeeping. In particular, this abstract class provides:
- A private distance matrix (dist) that
represents the distance from any grid square to the closest,
appropriate goal (black or one that matches the color of the robot).
A value of
MAX_VALUE indicates that an obstacle or a dead robot
is located at this grid square.
A value of
-1 indicates that there is no goal that can be
reached from that location.
- A predefined method (SetDist()) that takes information about the
obstacles, goals, and robots and fills in the distance data structure
with the appropriate values. Each thread should call this method before
each step is taken by the corresponding robot. It is your responsibility
to decide the direction of movement of the robot based on this information.
- An implementation of a semaphore object.
This is defined in
Semaphore.java. This is the only
synchronization primitive that you should use (unless you talk to
the Instructor first).
What to Hand In
Place in your Hand-In directory the following:
- An appropriately documented set of java source files (e.g., "lab2.java")
- An executable class that you have compiled for and tested on the elnux
system. We will test this executable using shell
commands similar to the following: "
java lab2 map1.txt"
- A "README" file (raw ASCII format) that contains:
- Your name(s)
- Your UID(s)
- The name of your top-level java class
- The answers to the following questions:
- What components of the data structures and what operations
did you protect with semaphores?
- Explain what happens when you execute your program with
the map specified by
map.strange.txt. If it fails, explain why.
you use operating systems techniques in order to prevent this failure from
happening (for this specific map, not for any map)? If it does not
fail, explain how you might use operating systems techniques to make
things more efficient (i.e., prevent one robot from having to
- What do you expect would happen if you removed the synchronization
mechanisms that you have used?
- How much time did you spend (collectively) on this assignment?
We will test your program with a variety of maps, including:
Checking Your Results
We will use output log file to partially check the correctness of
your program. So - it is essential that your log file be in
the appropriate format. This format is as follows:
<file> := <line>+
<line> := <N> goals left; <robot>+
<N> := [0...9]+
<robot> := <DEAD>? Robot<N> <color> At [<N>,<N>];
<DEAD> := (Dead)
<color> := Red | Green | Blue
- There is exactly one line for each logged time step.
- Each line contains an entry for each robot
We have provided the
checklog script that will scan your
log.txt file and confirm that no two robots ever
occupy the same grid square; and that all goals are consumed by the end
of the run. The script terminates with an "ALL OK" if these two conditions
We will also use this script to check your programs (but note that
this will not be the only check that we do).
Here are the log files that our program generates:
Here are some log files that our program generates when
proper thread synchronization is not used:
- You must use the implementation of Abstract_Robot, Goal, and
Map_Data. Do not change their implementation, as we've made some
things hard on purpose.
- Much of the CPU time will be spent in SetDist(). Since this
routine largely makes use of data that is not changing (the only
exception is the status of a goal), then it is safe to call this
method without performing synchronization operations. Doing so would
mean that you would largely serialize the path planning operation with
respect to the set of robots. So - I suggest that you do not.
- Technically, to be absolutely correct, it is necessary to call
SetDist() before each step is taken by a robot. However, this can be
very inefficient (N * X^2 * Y^2 for all robots).
In our implementation, we trade absolute
correctness for efficiency by only calling SetDist() under two conditions:
- When a goal that
is edible by a robot is eaten (whether this is by the robot itself
or by another robot).
- At random times (on average 1 out of 10 steps).
This latter trick means that a robot may not immediately respond to a change
in the situation (e.g., other robots completing their goals & dying),
but it will continue appropriately within a small number of steps.
- There is an odd condition that occurs when a robot dies on top
of another robot's goal without eating it. This prevents the goal
from being consumed by the right robot. You can choose to handle
this case or not.
- There is another odd condition where one or more robots will
die in such a way that the path is blocked for other robots. Again,
you can choose to handle this case or not (this one is harder than
the one above).
- Depending upon your implementation, the behavior of your robots
(and hence your log files) will be different that the ones that we give.
In addition, there may be some legitimate reasons that your program
does not complete with all goals eaten (we are not using the most
sophisticated path planner here). We anticipate that your program
should not have difficulties except where otherwise noted (since
ours does not have trouble); if there are any questions about correct
behavior, please send email.
This page is online at http://www-all.cs.umass.edu/~fagg/classes/377/labs/lab2/index.html
Andrew H. Fagg