JDK-4329256 : interrupted thread no longer produces output (sol)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 1.3.0,5.0,6
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: solaris_2.5.1,solaris_8,solaris_9
  • CPU: generic,sparc
  • Submitted: 2000-04-10
  • Updated: 2011-06-10
  • Resolved: 2011-06-10
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Description
Name: rlT66838			Date: 04/10/2000


java version "1.3.0rc2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0rc2-Y)

An interrupted thread appears not to be able to produce output through
System.err and System.out. This remains the case while the 'interrupted' flag is
set. Once the 'interrupted' flag has been cleared (either by a call to
Thread.interrupted or to an 'interruptable' method such as Thread.sleep), the
thread produces output normally.

Below is a Java class that demonstrates the problem.'java InterruptBug yes' runs
a thread which is (immediately) interrupted. 'java InterruptBug no' runs a
thread which is not interrupted. In both cases the thread repeatedly (once every
second, for five seconds) checks and prints the outcome of 'isInterrupted()'.
After that, it does the same for 'interrupted()'.

The 'no' version will produce correct output during the full ten seconds.
However the 'yes' version will buffer its output during the first five or six
seconds. Then, after clearing the 'interrupted' flag, the buffered output is
printed, followed by the remaining output.

Output of the 'no version':

bash-2.00$ java InterruptBug no
not interrupting
isInterrupted: false
isInterrupted: false
isInterrupted: false
isInterrupted: false
isInterrupted: false
interrupted: false
interrupted: false
interrupted: false
interrupted: false
interrupted: false

Output of the 'yes' version:

bash-2.00$ java InterruptBug yes
interrupting
isInterrupted: true
isInterrupted: true
isInterrupted: true
isInterrupted: true
isInterrupted: true
interrupted: true
interrupted: false
interrupted: false
interrupted: false
interrupted: false

Note that the 'yes' output shown above is correct. However there is a delay,
which is incorrect.

public class InterruptBug implements Runnable
{
  public static void main (String[] args)
  {
     if (args.length != 1) {
       System.err.println ("Usage: InterruptBug [yes|no]");
       return;
     }
     boolean do_interrupt = "yes".equals (args [0]);

     Thread t = new Thread (new InterruptBug());
     t.start();
     if (do_interrupt) {
       System.out.println ("interrupting");
       t.interrupt();
     }
     else
       System.out.println ("not interrupting");
  }

  public void run()
  {
    // for five seconds, repeatedly check the 'interrupted' flag
    for (int j = 0; j < 5; j++) {
      sleep (1000);
      if (Thread.currentThread().isInterrupted())
        System.out.println  ("isInterrupted: true");
      else
        System.out.println  ("isInterrupted: false");
    }

    // for five seconds, repeatedly check and clear the 'interrupted' flag
    for (int j = 0; j < 5; j++) {
      sleep (1000);
      if (Thread.interrupted())
        System.out.println  ("interrupted: true");
      else
        System.out.println  ("interrupted: false");
    }
  }

  /** Sleep without using Thread.sleep. */
  private static void sleep (long millis)
  {
    long end_time = System.currentTimeMillis() + millis;
    while (System.currentTimeMillis() < end_time)
      ;
  }
}
(Review ID: 103479) 
======================================================================

Name: yy116575			Date: 02/15/2001

Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0)
Java HotSpot(TM) Client VM (build 1.3.0, mixed mode)


Calling System.out.println clears the interrupted flag for the thread.

The following example code has the following output:

Before => true
After => false

public class junk {
  public static void main(String[] args) {
    try {
      Thread.currentThread().interrupt();
      boolean keepInterrupted = Thread.currentThread().isInterrupted();

      System.out.println();   // This blank line disappears.
      System.out.println("Before => " + keepInterrupted);
      System.out.println("After => " + Thread.currentThread().isInterrupted());

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
(Review ID: 117079) 
======================================================================

In the first test case, if the output is flushed then it appears immediately
but that seems to clear the interrupted status:
> java InterruptBug yes
interrupting
isInterrupted: true
isInterrupted: false
isInterrupted: false
isInterrupted: false
isInterrupted: false
interrupted: false
interrupted: false
interrupted: false
interrupted: false
interrupted: false

This doesn't occur on Linux.

michael.mccloskey@eng 2001-04-03

Comments
EVALUATION Legacy interruptible I/O has been disabled on Solaris in jdk7 so this is no longer an issue.
10-06-2011

EVALUATION The behavior described is only relevant to Java SE on Solaris. Sun's Java SE for Linux and Windows will never exhibit this behavior. There are two problems mentioned: 1) Thread interruption state being cleared by I/O such as println. This was a bug fixed long ago and if the number can be found it will become a "see also." 2) Thread interruption preventing I/O such as a println from completing. This behavior is still present with Java SE on Solaris. See the eval of 4385444 and the "work around" section of this bug for more details about avoiding this behavior or dealing with it properly if it cannot be avoided.
16-02-2006

WORK AROUND Use java.nio.channels.InterruptibleChannel and its fully portable, precisely specified and implemented exception mechanisms when I/O interruption is required. This is actually a workaround for this bug's opposite (i.e. non-Solaris Java SE lacks some forms of interruptible I/O), but it's worth repeating here. Avoid interrupting a thread that isn't prepared for it. Use properly synchronized state to coordinate thread interruption precisely. The "disappearing println" behavior in the description is only one of many hazards! Avoid continuously interrupting another thread without coordination. Although thread interruption state is cleared as part of entry to an InterruptedException catch block, a second interruption happening after the first while in the block will sabotage I/O attempted there. This should be the end of workaround discussion for Java applications. But the following details are provided for understanding issues surrounding attempts to cope with I/O interuption. For a legacy program misbehaving on Solaris because of I/O interruption that cannot be modified to use java.nio.channels and for which interruption cannot be properly managed, various strategies can be used, depending on the requirements of the application. These strategies may be aimed at the following goals: 1) Reliably detecting thread interruption 2) Reliably detecting I/O interruption 3) Attempting to complete I/O in the presence of interruption There are constraints that make these goals difficult to achieve. The third goal is particularly challenging for the general case. Thread interruption might be taking place at completely arbitrary times and with arbitrary short term timing. Thread scheduling effects can cause two apparently widely separated interruption calls by another thread to take place close in time from the perspective of the interrupted thread, as the interrupted thread might be blocked at arbitrary points. This is another way of saying the interrupting thread should reliably communicate with the interrupted thread and never blindly repeat interruption because this undercuts important assumptions covered later. I/O interruption events are "consumed" and translated back into thread interruption by PrintStream and PrintWriter classes. This is documented in PrintStream.checkError as follows: "If an operation on the underlying output stream throws an InterruptedIOException, then the PrintStream converts the exception back into an interrupt by doing: Thread.currentThread().interrupt(); or the equivalent." PrintWriter behavior is the same as PrintStream in this regard. There should be a corresponding note in the PrintWriter.checkError doc, but there isn't. Additional complications with these two classes are discussed later. Streams may be buffered, with varying buffer sizes and this interacts both with actual data parameters involved with I/O operations and with the way in which interruption manifests. In the worst case, interruption of a stream must be assumed to corrupt the stream and with arbitrary data loss. An interrupted stream should be discarded, and if there is no means of establishing a replacement stream the situation is unrecoverable. Although checkError holds to its contract of not returning true for an InterruptedIOException, it does an internal flush() of the stream, and when thread interruption is present, this results in an internal catch of IOException. Because this parent exception is caught rather than InteruptedIOException, "trouble" is declared and checkError ends up returning true. And because InterruptedIOException is not fielded in this case, thread interruption is not regenerated: it's left in the "cleared" state. So an interrupted PrintStream or PrintWriter operation such as PrintStream.println() cannot be disambiguated with an invocation of checkError: it's impossible to distinguish interruption from some other error condition. This means that checkError can only be used after Thread.interrupted or Thread.isInterrupted has been used to try to determine if interruption took place. However this is just a "try." Clear information is simply not available because thread interruption might have taken place after successful completion of a method of one of these classes (such as println) but before the test for interruption. Finally, defensive code attempting to compensate for interruption tends to introduce a risk of accidental dependency on Solaris-specific behavior. For all these reasons there may only be the illusion of an alternative to avoiding unintended interruption of I/O in the first place!
16-02-2006