JDK-4206767 : Threads that block on I/O do not let other equal priority threads run.
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 1.1.6
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: generic
  • CPU: generic
  • Submitted: 1999-01-28
  • Updated: 2001-10-15
  • Resolved: 2001-07-16
Description

Name: dbT83986			Date: 01/28/99


Note that I am part of the Linux Java porting group.  This problem
also exists on Solaris (albeit not with native threads)

This happens with jdk releases for Linux 1.1.3 to 1.1.6 (these are the only
versions I have tried - it may happen elsewhere too)

This happens with jdk releases for FreeBSD (1.1.5 is confirmed)

This does not happen with the JDK 1.1.x releases on Windows.

This does not happen with Kaffe 0.92 or 0.100 using the official JDK classes.zip

Ok - enough intro - here is the problem:

Ready threads will not run even when the active thread blocks on reading
input *unless* the active thread is of a lower priority when it blocks.

The attached code shows this very well.  The results should be that the
output happens even as you can enter data from STDIN.  However, under the
Linux JDK port this does not happen.  Output only happens once you enter ctrl-D
to close the stream.

However, the main thread is blocked at a BufferedInput.readLine() method call.
In fact, no noticeable CPU is used during this time so the JVM is blocked       somewhere and yet the other thread should be running.  (The sleep in that       thread's loop is just to slow it down a bit.  It is not needed)

Since the system can run "correctly" when you lower the priority of the thread
that is doing the reading from STDIN, it would indicate that it is not a technical
problem in the JVM that prevents task switching during a read from STDIN but a
problem of the JVM itself.  My guess is that somehow the thread is not marked
as "blocked" when the JVM blocks on the input.  Thus, the higher priority
threads get to run because they are higher priority and not because the input
thread has blocked.

/**
 * This example shows how threads seem to get blocked
 * even though there is no other running thread available.
 * However, if you lower the priority of the thread that is
 * about to block, this example works just fine.
 * <p>
 * The issue is that threads of equal priority do not get
 * a chance to run if the active thread just blocked on I/O.
 * <p>
 * The example here should product output while you are
 * entering data into the system.  Every time a new line
 * is entered a message will print that it was received.
 * This is just to show that the main thread is working.
 * <p>
 * Causing an end-of-file (ctrl-D on UNIX, ctrl-Z on Windows)
 * will cause the input loop to exit and the counting loop
 * will exit when it finished its counting.
 * <p>
 * If you enter any argument after the "java showbug" command
 * the code will lower the priority of the input loop just
 * before it starts to run.  This lets the two threads run
 * at the same time which means that the blocking on the
 * input is not a mechanical issue within the JVM since it
 * does not cause the blockage when run at a lower priority.
 * <p>
 * This problem does not show itself on the JDK 1.1.x releases
 * for Windows NT (or Windows 95)
 * <p>
 * This problem does not show itself using Kaffe on Linux or
 * FreeBSD (the only places I checked) using the JDK 1.1.x
 * classes.zip file.
 * <p>
 * This problem *does* show up in the Linux JDK 1.1.3 to
 * JDK 1.1.6 releases from Blackdown.org.  (These are the
 * only versions I have tested.  Others may also have this
 * problem.)  This is on the Intel version of Linux.
 * <p>
 * This problem *does* show up in the FreeBSD JDK 1.1.5
 * (the only version I tested)
 * <p>
 * I have not had a chance to try this code on Solaris (yet)
 * but I would think that it would not be a problem due to
 * the fact that the JDK now uses native threads on Solaris.
 */
class showbug extends java.lang.Thread
{
	showbug(String name)
	{
		super(name);
	}

	/**
	 * This should run until it is done.  Right now
	 * it has lots of "sleep" time in it so as to not
	 * output too fast.  However, this is not required
	 * in order to show the problem.  Just increase the
	 * loop count first so that you have time to play
	 * with the input.
	 */
	public void run()
	{
		try
		{
			for (int i=0;i < 30;i++)
			{
				java.lang.System.out.println(getName() + ": " + i);

				// You can remove this sleep if you want to
				// It does nothing other than slow down the
				// output.  The behavior will remain the same.
				java.lang.Thread.sleep(500);	// Sleep 1/2 of a second.
			}
		}
		catch (java.lang.InterruptedException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * This is the main thread.  It creates the background
	 * counting/output thread, sets up the BufferedReader,
	 * starts the counting thread, and then goes into a loop
	 * reading lines from STDIN until EOF.
	 * <p>
	 * Note that with that small sleep() in the code, the
	 * counting thread gets a moment to run before this one
	 * wakes up again and blocks again on input.
	 */
	public static void main(String[] args) throws java.lang.Throwable
	{
		// Build one of my test threads...
		showbug test=new showbug("Testing");

		// Get a BufferedReader object for STDIN
		java.io.BufferedReader input=new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.System.in));

		// Start the thread...
		test.start();

		// If there is an argument then set the priority low...
		if (args.length > 0)
		{
			// By setting the priority of this thread down
			// below that of the test thread, the blocking
			// on input does not prevent the other thread
			// from running.
			java.lang.Thread.currentThread().setPriority(java.lang.Thread.MIN_PRIORITY);
		}

		// Just loop around until EOF...
		while (input.readLine() != null)
		{
			java.lang.System.out.println("Got a line of input.");

			// Note that if you put in this sleep here, some
			// data will output per line of input...
			// (Unless you lower the thread priority...
			// in which case it works just fine.)
			//java.lang.Thread.sleep(100);	// 1/10 of a second...
		}

		java.lang.System.out.println("Exited input loop...");
	}
}
(Review ID: 34246)
======================================================================

Comments
WORK AROUND Name: dbT83986 Date: 01/28/99 See code above. The workaround seems to be to lower the priority of the thread that is about to block. However, this is not usually an acceptable solution as it has other impacts. ======================================================================
11-06-2004

EVALUATION Note that the problem is claimed to exist on Solaris green threads. william.maddox@Eng 1999-02-11 This bug is specific to green threads, which were only supported in the Classic VM, which is itself no longer supported. -- mr@eng 2001/7/16
11-02-1999