Name: fh87463 Date: 10/19/2001
Test DiningPhilosophers test hung when running with merlin JDK after(include) build 70
on solsparc/solx86/linux platform. It will pass when running with ladybird and merline
JDK before build 69.
All source file and tonga log located under:
/net/jano.sfbay/export/disk20/GammaBase/Bugs/[BugID]
To reproduce the bug:
=====================
1. cd /net/jano.sfbay/export/disk20/GammaBase/Bugs/[BugID]
2. go into one of the sub directory
3. run *tlog file.
Here is java source code:
/******************************************************************************
DiningPhilosophers.java
This class tests Thread synchronization by means of a classic
Operating Systems problem called Dining Philosophers.
Description of Analogy:
A given number of Philosophers spend their lives thinking and
eating around a large table with a bowl of rice in the middle. Each
Philosopher eats with chopsticks - and the Philosophers must share
their chopsticks. In the classic scenario, each Philosopher has one
chopstick on his/her right and one chopstick on his/her left.
Because we are dealing with a finite machine, and not a theoretical
structure, I've decided to fix the number of chopsticks to two. There
may be one hundred Philosophers. They all have to share one pair of
chopsticks. I did this because I wanted to see how well the system keeps
track of things when there are a lot of Threads waiting by means of
synchronization.
In both the classic scenario and my own, a Philosopher must have both
chopsticks in order to eat.
Implementation :
The number of Philosophers who share the two chopsticks is given
as a command-line argument. The program reads the String in, converts it
to an integer value, and then uses that value to determine the number of
Philosophers to create.
Here, I've decided to implement Philosophers as Threads, each of
which have to use both Chopsticks to eat a certain number of "turns".
The actual "Chopsticks" are not implemented as variables in the program.
Instead, to keep everything simpler, I decided to use two boolean arrays,
"left_chopstick_used" and "right_chopstick_used" for the Philosopher Threads
to synchronize on. Both arrays are two-dimensional, with the first dimension
corresponding to the number of concurrent Philosopher Threads, and the second
dimension corresponding to the number of times that Philosopher has eaten.
Every time a Philosopher gains the monitor for one of
these arrays, the Philosopher is said to have grabbed a Chopstick.
Each time a Philosopher Thread gains the monitor for an array, it sets
the value of that array at that index to "true."
Every time a Philosopher Thread gains both Chopsticks, that Philosopher
eats. To represent this, I used a one-dimensional boolean array - "has_eaten"
with a number of entries equal to the number of Philosopher Threads times the
number of turns each Philosopher takes. Each time a Philosopher eats, he
sets the value of one of the indices in "has_eaten" to "true." He also
increments the static int index of that "has_eaten" array. The name of this
static int index is "has_eaten_index" - I am not endeavoring to be terribly
imaginative with names here!!
Note that neither the "has_eaten" array nor its static "has_eaten_index"
have any kind of synchronization code around them. This apparently dangerous
omission has a practical application: only one Philosopher Thread at a time
gets access to both Chopsticks, and therefore only one Philosopher Thread
at a time gets access to the "has_eaten" array, and can only increment its index
in an appropriate fashion if the synchronization code is working according to
the specifications. If the synchronization code does not exclude other
Philosopher Threads, the chances are overwhelming that the public static
int "has_eaten_index" will be incremented in an inappropriate manner, and
therefore the test will fail.
At the very end of the test, after all the Philosopher Threads have
run and died, the "main" goes through all the arrays, checking their boolean
values. if it finds one of these values set to "false", it will exit
with status = 1 (FAIL). If the exit(1) conditions don't trigger, the
test is assumed to have passed, and the message "TEST PASSED" is printed
out for the reader.
*****************************************************************************/
import java.lang.*;
import java.io.*;
/*
"DiningPhilosophers" class
This public class contains the main() which gets everything going
and contains all the public arrays and variables which it checks at the end.
*/
public class DiningPhilosophers{
public static boolean[][] used_left_chopstick;
public static boolean[][] used_right_chopstick;
public static boolean[] has_eaten;
public static int has_eaten_index = 0;
public static int NUMBER_OF_PHILOS;//determined from command-line argument
public static final int NUMBER_OF_TURNS = 100000;//constant - we want it big
public static void main(String argv[]){
NUMBER_OF_PHILOS = Integer.parseInt(argv[0]);
used_left_chopstick = new boolean[NUMBER_OF_PHILOS][NUMBER_OF_TURNS];
used_right_chopstick = new boolean[NUMBER_OF_PHILOS][NUMBER_OF_TURNS];
has_eaten = new boolean[NUMBER_OF_PHILOS*NUMBER_OF_TURNS];
//initialize - if any Thread skips one of these, we want it to fail
for(int i=0; i<NUMBER_OF_PHILOS; i++){
for(int j=0; j<NUMBER_OF_TURNS; j++){
used_left_chopstick[i][j] = false;
used_right_chopstick[i][j] = false;
has_eaten[i*j+j] = false;
}//end inner for
}//end outer for
for(int i=1; i<=NUMBER_OF_PHILOS; i++){
new Philosopher(i).start();
}//end for
//wait until all Philosopher Threads have finished.
while (Thread.activeCount()>1)
Thread.currentThread().yield();
//now, we check all three boolean arrays for any values remaining at "false"
for(int i=0; i<NUMBER_OF_PHILOS; i++){
for(int j=0; j<NUMBER_OF_TURNS; j++){
if(! used_left_chopstick[i][j])
System.exit(1);
if(! used_right_chopstick[i][j])
System.exit(1);
if(! has_eaten[i*j+j])
System.exit(1);
}//end inner for
}//end outer for
//if we got to this point, the test passed
System.out.println("TEST PASSED");
}//end main
}//end public class DiningPhilosophers
/*
"Philosopher" class
This class extends Thread, so it can run like a Thread. It
uses synchronization to ensure exclusive access to the "has_eaten" boolean
array and its "has_eaten_index"
*/
class Philosopher extends Thread{
private int index;
//constructor
public Philosopher(int index){
super(String.valueOf(index));
this.index = index;
}//end constructor
public void run(){
for(int steps=0; steps < DiningPhilosophers.NUMBER_OF_TURNS; steps++){
synchronized(DiningPhilosophers.used_left_chopstick){
DiningPhilosophers.used_left_chopstick[index-1][steps] = true;
synchronized(DiningPhilosophers.used_right_chopstick){
DiningPhilosophers.used_right_chopstick[index-1][steps] = true;
DiningPhilosophers.has_eaten[DiningPhilosophers.has_eaten_index++] = true;
}//end synchronized - DiningPhilosophers.used_right_chopstick
}//end synchronized - used_left_chopstick
}//end for
}//end public void run() method
}//end non-public Philosopher class
======================================================================