JDK-4517307 : (thread) DiningPhilosophers test hung - Thread.activeCount == 2 at program start
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: solaris_8
  • CPU: sparc
  • Submitted: 2001-10-19
  • Updated: 2005-10-25
  • Resolved: 2005-10-25
Related Reports
Duplicate :  
Description
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



======================================================================

Comments
EVALUATION Worksgreat with JDK 1.3.1, threads seem to sleep in 1.4 JDK ###@###.### 2001-10-22 This program hangs because the Thread.activeCount starts out at 2 when using the Merlin JDK libraries. The count never drops to 1 and so the program never terminates. //wait until all Philosopher Threads have finished. while (Thread.activeCount()>1) Thread.currentThread().yield(); The simpler program below will print out the initial thread count of 2. // Hello World class Hello { public static void main (String args[]) { System.out.println("Initial Thread.activeCount is " + Thread.activeCount() ); } } I do not believe that this is a VM problem because I installed a Merlin VM into a 1.3 JDK tree and the program ran successfully. (wabi)/space/jdk1.3/bin> ./java_g -server -version java version "1.3.0" Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C) Java HotSpot(TM) Server VM (build 1.4-internal-debug, mixed mode) (wabi)/space/jdk1.3/bin> ./java_g -server Hello Initial Thread.activeCount is 1 Here is the failing run: (wabi)/space1/jdk_b83/bin> ./java_g -server -version java version "1.4.0-beta3" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta3-b83) Java HotSpot(TM) Server VM (build 1.4.0-beta3-b83-debug, mixed mode) (wabi)/space1/jdk_b83/bin> cp /tmp/Hello.class . (wabi)/space1/jdk_b83/bin> ./java_g -server Hello Initial Thread.activeCount is 2 ###@###.### 2001-10-23 The current behavior is less than optimal and should probably be fixed. However, the primary fault lies with the test program, which should not be using Thread.activeCount in this fashion. There is no guarantee that it will be one. There could be one or more background threads in the main thread group performing various system activities. Incidentally, another problem with ths program is that the main thread busy-waits: //wait until all Philosopher Threads have finished. while (Thread.activeCount()>1) Thread.currentThread().yield(); This is unnecessary and inefficient. ###@###.### 2001-11-16
16-11-2001