Customer comments follow (source code file with example file attached):
Problem 1: Threads blocked on input do not seem to leave the "running"
state. As a consequence, if input is high priority (a good idea), all
other threads will COMPLETELY starve.
The demo program shows this very clearly. It fails under Solaris
2.5.1, and under Linux. It does NOT fail under Solaris 2.6. I
suspect the reason for 2.6 success is NOT that the bug is fixed, but
that multiple real threads mask the problem. Success under 2.6 does
not help much, as our program will need to operate on 2.5 systems.
Problem 2: Inputs from *multiple* sources, especially if one blocks for
a while (e.g. tty) seems to starve the system.
***********************************************************************
******************** SAMPLE FILE FOLLOWS ******************************
***********************************************************************
import java.io.*;
/**
* JAVA BUG REPORT
*
* SUMMARY: Blocked threads do not leave the running state.
*
* QUALIFICATION:
*
* I *am* an experienced threads programmer, have studied the book,
* 'Java Treads' by Oaks and Wong, and have given Henry Wong a bug
* report which he is going to incorporate into the next version. (I
* might be wrong about this bug, but "I don't think so").
*
* DETAIL:
*
* Testing shows that when a thread is blocked on file input, there
* *is* a thread scheduling event, which allows other threads to run.
* However, the state of the blocked thread *does not* change from
* running to blocked.
*
* As a consequence, if the reader were a high priority thread (good
* idea for responsiveness) lower priority threads WILL starve.
*
* The impact of this problem will be critical to our "system
* software", a simulator which needs to be responsive to user input.
*
* I have tested (separately) and determined this is NOT the case for sleep.
* That is, when sleeping, threads appear to enter the 'blocked' state,
* with all the expected behavior.
*
* TESTING SCENARIO:
*
* An object is shared between main, which starts a reader thread.
* After completing input, the reader sets the flag. Main interrogates
* that flag upon return from start(). He prints his bug analysis
* conclusion.
*
* 1. When all threads are at the same priority, no starvation occurs.
*
* The first "runner" blocks awaiting input, and main returns from
* start(). This is what one would normally expect.
*
* 2. When the reader is at a high priority, STARVATION occurs.
*
* main does not continue until after input completes.
*
* CUSTOMER IMPACT:
*
* We are writing a simulator which will need to be responsive to user
* input. With this bug, we cannot have the reader at high priority,
* and as a consequence the simulator will *not* be responsive.
*
* This is our first major java project. This sort of failure will
* make it very hard to convince management that this is the language
* of choice!
*
* WORKAROUND:
*
* (unacceptable accept for testing) -- run reader threads at low
* priority.
*/
public class Starved
{
static void doIt(Runner runner, DoneFlag runner_done)
throws InterruptedException
{
System.out.println(
"MAIN: Starting " + runner.desc()+ " now, I will check for the \n"+
" bug after start() returns.");
runner_done.flag = false;
runner.start();
synchronized (runner_done) {
if (runner_done.flag) {
System.out.println(
"\n****************************************************\n"+
" ******* BUG FOUND *******\n"+
"*** The runner completed his input BEFORE main returned "+
"from start. ***\n"+
"*** This is a bug! Threads pending input should be in\n"+
"*** the BLOCKED state. Main should have continued.\n"+
"****************************************************\n");
} else {
System.out.println("\n*** OK, the bug did not manifest. ***");
}
}
runner.join();
} // doIt
public static void main(String[] args) throws InterruptedException {
DoneFlag runner_done = new DoneFlag();
Runner high_prio = new Runner("high prio", runner_done);
Runner norm_prio = new Runner("norm prio", runner_done);
high_prio.setPriority(Thread.currentThread().getPriority() +1);
System.out.println(
"\n\n************ JAVA Scheduling BUG demo program ***********\n"+
"This program demonstrates a bug in the VM thread scheduling.\n"+
"Threads pending input are not placed in the \"blocked\" state!\n"+
"As a consequence, a high priority \"reader\" will STARVE the system.\n\n"+
"This program runs a test twice. Each test waits on a line of\n"+
"input from the user (hence blocks for input). Meanwhile, main\n"+
"**should** return from runner.start(), where he checks a\n"+
"synchronized flag to see who finished first. If the bug\n"+
"appears, it will loudly complain.\n\n"+
"Correct result: The bug should not manifest; start() should\n"+
" return prior to blocked input completing.\n"+
"Expected result: The high priority runner will manifest the bug.\n"+
" This is starvation. Because the equal priority runner does\n"+
" not manifest this, I conclude I/O *does* cause a \"scheduling\n"+
" event\", but it does not leave the \"running\" state.\n\n"+
"Previous test results:\n"+
" This bug manifests in jdk1.1.1 on solaris 2.5.1 and \n"+
" linux 2.0.30. It does *not* manifest on solaris 2.6 (we are\n"+
" in the early access program) (I suspect native threads in\n"+
" 2.6 masks the bug).\n"+
"************ JAVA Scheduling BUG demo program ***********\n"
);
System.out.println("Main is running at priority --> "+
Thread.currentThread().getPriority());
doIt(norm_prio, runner_done);
doIt(high_prio, runner_done);
} // main
} // class Starved
class Runner extends Thread
{
String v_name;
DoneFlag v_done_flag;
Runner(String my_name, DoneFlag done_flag) {
super();
v_name = my_name;
v_done_flag = done_flag; // only change when leaving run()
}
public String desc() {
return ("--" + v_name + " at prio " +getPriority() + "-- ");
}
public void run() {
try {
BufferedReader rdr = new BufferedReader(
new InputStreamReader(System.in));
System.out.println(desc() + " -- waiting on line of input --");
String line = rdr.readLine();
synchronized (v_done_flag) {
// set flag. caller can thus tell if we completed
// input before the return from start()
v_done_flag.flag = true;
}
System.out.println(desc() +
"I got my input, I am OUT OF HERE:\n (" +
line + ").");
} catch (IOException e) {
System.out.println(desc() + " Exception:" + e);
}
} // run
} // class Runner
class DoneFlag
{
public boolean flag = false;
}
*************************************************
*********** END SAMPLE CODE *********************
*************************************************
More info from customer:
> We have done additional testing on the thread starvation problem I
> reported earlier.
>
> We have compiled the public Java Virtual Machine, Kaffe (v0.9.0).
>
> The bug does *not* manifest under that VM. (under Solaris 2.5.1)
>
> Brian
Problem 1: Threads blocked on input do not seem to leave the "running"
state. As a consequence, if input is high priority (a good idea), all
other threads will COMPLETELY starve.
The demo program shows this very clearly. It fails under Solaris
2.5.1, and under Linux. It does NOT fail under Solaris 2.6. I
suspect the reason for 2.6 success is NOT that the bug is fixed, but
that multiple real threads mask the problem. Success under 2.6 does
not help much, as our program will need to operate on 2.5 systems.
Problem 2: Inputs from *multiple* sources, especially if one blocks for
a while (e.g. tty) seems to starve the system.
***********************************************************************
******************** SAMPLE FILE FOLLOWS ******************************
***********************************************************************
import java.io.*;
/**
* JAVA BUG REPORT
*
* SUMMARY: Blocked threads do not leave the running state.
*
* QUALIFICATION:
*
* I *am* an experienced threads programmer, have studied the book,
* 'Java Treads' by Oaks and Wong, and have given Henry Wong a bug
* report which he is going to incorporate into the next version. (I
* might be wrong about this bug, but "I don't think so").
*
* DETAIL:
*
* Testing shows that when a thread is blocked on file input, there
* *is* a thread scheduling event, which allows other threads to run.
* However, the state of the blocked thread *does not* change from
* running to blocked.
*
* As a consequence, if the reader were a high priority thread (good
* idea for responsiveness) lower priority threads WILL starve.
*
* The impact of this problem will be critical to our "system
* software", a simulator which needs to be responsive to user input.
*
* I have tested (separately) and determined this is NOT the case for sleep.
* That is, when sleeping, threads appear to enter the 'blocked' state,
* with all the expected behavior.
*
* TESTING SCENARIO:
*
* An object is shared between main, which starts a reader thread.
* After completing input, the reader sets the flag. Main interrogates
* that flag upon return from start(). He prints his bug analysis
* conclusion.
*
* 1. When all threads are at the same priority, no starvation occurs.
*
* The first "runner" blocks awaiting input, and main returns from
* start(). This is what one would normally expect.
*
* 2. When the reader is at a high priority, STARVATION occurs.
*
* main does not continue until after input completes.
*
* CUSTOMER IMPACT:
*
* We are writing a simulator which will need to be responsive to user
* input. With this bug, we cannot have the reader at high priority,
* and as a consequence the simulator will *not* be responsive.
*
* This is our first major java project. This sort of failure will
* make it very hard to convince management that this is the language
* of choice!
*
* WORKAROUND:
*
* (unacceptable accept for testing) -- run reader threads at low
* priority.
*/
public class Starved
{
static void doIt(Runner runner, DoneFlag runner_done)
throws InterruptedException
{
System.out.println(
"MAIN: Starting " + runner.desc()+ " now, I will check for the \n"+
" bug after start() returns.");
runner_done.flag = false;
runner.start();
synchronized (runner_done) {
if (runner_done.flag) {
System.out.println(
"\n****************************************************\n"+
" ******* BUG FOUND *******\n"+
"*** The runner completed his input BEFORE main returned "+
"from start. ***\n"+
"*** This is a bug! Threads pending input should be in\n"+
"*** the BLOCKED state. Main should have continued.\n"+
"****************************************************\n");
} else {
System.out.println("\n*** OK, the bug did not manifest. ***");
}
}
runner.join();
} // doIt
public static void main(String[] args) throws InterruptedException {
DoneFlag runner_done = new DoneFlag();
Runner high_prio = new Runner("high prio", runner_done);
Runner norm_prio = new Runner("norm prio", runner_done);
high_prio.setPriority(Thread.currentThread().getPriority() +1);
System.out.println(
"\n\n************ JAVA Scheduling BUG demo program ***********\n"+
"This program demonstrates a bug in the VM thread scheduling.\n"+
"Threads pending input are not placed in the \"blocked\" state!\n"+
"As a consequence, a high priority \"reader\" will STARVE the system.\n\n"+
"This program runs a test twice. Each test waits on a line of\n"+
"input from the user (hence blocks for input). Meanwhile, main\n"+
"**should** return from runner.start(), where he checks a\n"+
"synchronized flag to see who finished first. If the bug\n"+
"appears, it will loudly complain.\n\n"+
"Correct result: The bug should not manifest; start() should\n"+
" return prior to blocked input completing.\n"+
"Expected result: The high priority runner will manifest the bug.\n"+
" This is starvation. Because the equal priority runner does\n"+
" not manifest this, I conclude I/O *does* cause a \"scheduling\n"+
" event\", but it does not leave the \"running\" state.\n\n"+
"Previous test results:\n"+
" This bug manifests in jdk1.1.1 on solaris 2.5.1 and \n"+
" linux 2.0.30. It does *not* manifest on solaris 2.6 (we are\n"+
" in the early access program) (I suspect native threads in\n"+
" 2.6 masks the bug).\n"+
"************ JAVA Scheduling BUG demo program ***********\n"
);
System.out.println("Main is running at priority --> "+
Thread.currentThread().getPriority());
doIt(norm_prio, runner_done);
doIt(high_prio, runner_done);
} // main
} // class Starved
class Runner extends Thread
{
String v_name;
DoneFlag v_done_flag;
Runner(String my_name, DoneFlag done_flag) {
super();
v_name = my_name;
v_done_flag = done_flag; // only change when leaving run()
}
public String desc() {
return ("--" + v_name + " at prio " +getPriority() + "-- ");
}
public void run() {
try {
BufferedReader rdr = new BufferedReader(
new InputStreamReader(System.in));
System.out.println(desc() + " -- waiting on line of input --");
String line = rdr.readLine();
synchronized (v_done_flag) {
// set flag. caller can thus tell if we completed
// input before the return from start()
v_done_flag.flag = true;
}
System.out.println(desc() +
"I got my input, I am OUT OF HERE:\n (" +
line + ").");
} catch (IOException e) {
System.out.println(desc() + " Exception:" + e);
}
} // run
} // class Runner
class DoneFlag
{
public boolean flag = false;
}
*************************************************
*********** END SAMPLE CODE *********************
*************************************************
More info from customer:
> We have done additional testing on the thread starvation problem I
> reported earlier.
>
> We have compiled the public Java Virtual Machine, Kaffe (v0.9.0).
>
> The bug does *not* manifest under that VM. (under Solaris 2.5.1)
>
> Brian
- duplicates
-
JDK-1237893 Solaris: block on read of System.in blocks all threads
- Closed