Introduction to resource and barrier synchronization

In this exercise you will practice creating threads and using semaphores to synchronize their behaviour. Again you will solve the same problems in both Java and MPD.

We will start from a program that opens a window in which a couple of "balls" start moving, bouncing against the walls.

Java version

Download the program to your working directory and unzip the file. You will find three classes: Compile and run the program:
javac *.java
java Balls
The program must be stopped by Ctrl-C. Look at the classes and make sure that you understand them.

Killing the balls

[Act] Your first task is to add still another thread to your program that kills the balls at random times (i.e. with short random delays inbetween). But the balls should be killed in random order. Killing a ball means that its run method must terminate. This should be achieved by making the loop terminate normally and NOT by calling the deprecated method to stop a thread. Further, the ball must be removed from the world (in a thread-safe way similar to addBalls). After this has been done the garbage collector in the run-time system will eventually reclaim the space allocated for the ball object.

To solve the problem you can make use of a semaphore that the balls try to acquire in order to "get permission to die". The semaphore is initially zero and then released a number of times in a thread started in main. Note that it is very useful to try to acquire the semaphore, using its tryAcquire method.

Implement this and test your program. Make sure that you understand how the design makes the killing order unpredictable.

If you want, you can change the behaviour of the program further so that killed balls are reborn after some (random) time. But don't spend all time on this exercise. You should also have some time both for the next Java exercise and the MPD versions.

Freezing the balls

Now return to the original version of the program (make a new directory and download the program again).

[Act] You must now modify the program to achieve the following behaviour: When a bouncing ball after one of its moves finds itself in the diagonal area of the world (i.e. where x is very close to y), it will "freeze", i.e. stop moving. Note that a ball may jump over the diagonal area in one move; this will not cause it to freeze. When all balls have frozen at the diagonal, they will all wake up and continue bouncing until they freeze again on the diagonal. This bouncing/freezing continues forever.

You should recognize this as a form of barrier synchronization that can be achieved using N+1 semaphores: one common barrier semaphore, which ball threads release when they reach the synchronization point, and an array of "continue" semaphores, indexed by thread, which threads acquire in order to continue beyond the barrier.

A special barrier synchronization process is also needed, which repeatedly acquire the barrier semaphore N times, followed by releasing all the continue semaphors. See slide 36 of lecture 6 for details.

Freezing revisited

[Act] The package java.util.concurrent includes the class CyclicBarrier, which provides more convenient means to achieve barrier synchronization. Rewrite the program from the previous exercise using this class instead of semaphores.

MPD version

We encourage you to do both the Java and MPD versions of this exercise to appreciate differences and similarities. First download and unzip the MPD program (Linux version, Solaris version). Compile it (just type make) and run it; it has the same behaviour as the Java version. Then look at the code, which again is very similar to the Java code. In addition to the three resources that correspond to Java classes, we have added two globals, one for handling coordinates in the world and one for simple handling of colours. This is intended to help you become familiar with MPD constructs. In fact, the MPD program now can easily generate random balls better than the Java program.

The window handling and graphics routines are of course different; you should NOT try to learn to do GUI programming in MPD unless you are particularly interested. In this course you will not be asked to do any such programming. Now you will solve the same two problems as in Java. However, we propose that you start with the second problem.

Freezing the balls

[Act] If you have solved the Java version of this problem, an MPD version using semaphores should be straightforward. Do it anyhow! You might need the try_p() (see below).

[Act] Now for a harder exercise: Implement the body of a resource cyclicBarrier which provides similar features as the Java class of the same name. But we are content with a simpler version with the following spec:

resource cyclicBarrier

  op await()

body cyclicBarrier(int parties) separate
The parameter parties is the number of threads that need to reach the barrier before they are all allowed to continue. Write the body part of this resource and then use it to solve the ball-freezing problem again.
Hint: We cannot follow the array-of-semaphores approach directly, since that would require await to have a parameter i indicating the index of the semaphore on which to block. Instead, one could try to use a single semaphore on which all processes block and an integer variable counting how many processes have reached the barrier. But this integer variable is shared, so we need to protect updates to it using a second, mutex semaphor. Thus, the structure of the resource body would be something like
body cyclicBarrier

  sem barrier = 0
  sem mutex = 1

  int arrived = 0

  proc await() {
    P(mutex)
    arrived++
    ...
  }

end

Note that, however, trying to complete this, you will get a faulty solution that does not prevent a quick process from "stealing" a V from a slower process. Optionally, you can fix this by using two barrier semaphores that are used in alternating turns. But, as we warned above, this is a bit tricky.

To test your implementation, modify the bouncing balls program to use this resource. Note that you must update the Makefile, since your MPD program now contains two more files, cyclicBarrier.spec.mpd and cyclicBarrier.body.mpd. This is done using mpdm:

mpdm coords.mpd colors.mpd ball.spec.mpd ball.body.mpd ballWorld.mpd cyclicBarrier.spec.mpd cyclicBarrier.body.mpd 
balls.mpd mpdwin.o -lX11
Then make clean will clean up the old compiled files, and make run will compile and run the main resource balls.mpd.

Killing the balls

[Act] If you have time, here is a final exercise. The task is to do the ball killing program in MPD.

Here you would also want semaphores with an operation similar to tryAcquire from the Java class. Such an operation is not available for MPD:s built-in semaphores. Therefore we provide you with an alternative semaphore resource. This is a formulation of a semaphore in MPD, which is not compatible with the native semaphores. Thus, you cannot use the P and V primitives.

To use the semaphores of this resource, write

import semaphore                          # import the package
...
cap semaphore mutex = create semaphore(1) # create a new semaphore
...
mutex.p()                                 # works as P
... Critical Section 
mutex.v()                                 # works as V

The resource also contains a non-blocking operation try_p() which is the correspondence to tryAcquire. It tries to acquire the semaphore but instead of blocking it returns immediately with return value true if the semaphore was acquired and false if it was not. (If you look into the file semaphore.mpd, it will be difficult to understand for the moment. Everything will be explained in a few weeks! For now, you should rely on your understanding of the concept of semaphores and trust that the required behaviour is implemented.)

Remember to rebuild the Makefile for this exercise, using mpdm:
mpdm coords.mpd colors.mpd ball.spec.mpd ball.body.mpd ballWorld.mpd semaphore.mpd balls.mpd mpdwin.o -lX11