Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4016179

java.io.PipedOutputStream.write methods hang if reader thread dies

XMLWordPrintable

    • 1.2beta4
    • sparc
    • solaris_2.5
    • Not verified

      This bug was found by St.Petersburg Java SQE team (by Stanislav Avzan).


      As follows from comments on bugs 1266819 and 1267045, the specs should
      be read as follows:
      "22.17.4 public void write(int b) throws IOException

      If a thread was reading data bytes from the connected piped input stream,
      but the thread is no longer alive <and write buffer is full>, then an
      IOException is thrown."

      The attempt of such approach is seen from source code. In fact, however,
      write call may wait infinitely for the reader that is already dead.
      Here is the test demonstraiting the bug:
      ----- test16.java ---------------------------------------
      import java.io.*;

      public class test16 {

        public static void main( String[] argv ) {
          PipedOutputStream os = new PipedOutputStream();
          PipedInputStream is= new PipedInputStream();
          LazyReader lr;
          int rl;
          try {
            is.connect(os);
           } catch(Throwable e) {
             System.out.println("Test failed: unexpected <"+e+"> thrown");
             System.exit(1);
           }
          byte[] data = new byte[1000];
          lr = new LazyReader(is,"Test",1);// create reader thread with minimal delay
          try {
            os.write(data); //step write 1000 bytes of data
            lr.start();//step start reading bytes of data
            try {
              Thread.sleep(1000); //step sleep for 1 second
            } catch (InterruptedException e1) {}
            if(!(
              is.available() == 0 //step test reader is active
            ))
             { System.out.println("Test failed: no preliminary readings performed" );
               System.exit(1);
             }
          }
          catch(Throwable e) {
             System.out.println("Test failed: unexpected <"+e+"> thrown");
             System.exit(1);
          }
        //here starts the core of the test
          try {
            lr.stop(); //step stop readerer thread
            System.out.println("Is producer alive? - "+lr.isAlive());
            try {
              Thread.sleep(1000); //step sleep for 1 second
            } catch (InterruptedException e1) {}
       // next line will execute forever
            for(int i=0;i<1025;i++) os.write(i); //step try to write into dead thread
              //remembering that buffer size is 1024
            System.out.println("Test failed: no exceptions thrown");
          }
          catch(IOException e) // test IOException is thrown
          { System.out.println("Test passed: IOException thrown");
          } catch(Throwable e) {
            System.out.println("Test failed: unexpected <"+e+"> thrown");
          }
        }
      }
      ------- LazyReader.java ---------------------------------
      import java.io.*;

      public class LazyReader extends Thread {
        private PipedInputStream snk;
        private String testid;
        private int delay;

        public LazyReader(PipedInputStream snk, String testid, int delay) {
          this.snk = snk;
          this.testid = testid;
          this.delay = delay;
        }
        
        public void run() {
          try {
            while(true) {
              try {
                Thread.sleep(delay);
              } catch(InterruptedException e) {
              }
              while(snk.available() > 0) snk.read(); //read all written
            }
          } catch(IOException e1) {
              System.out.println(testid + " failed: PipedOutputStream write error");
              System.exit(1);
          }
        }

      }
      ----- The output of the test: -------------------------

      $JAVA test16
      Is producer alive? - false
      ^C
      -------------------------------------------------------
      The test has to be interrupted
       
      Here is the fix I'd like to recommend:
      The reader and writer threads are represented in PipedInputStream
      class as readSide and writeSide.
      writeSide is initialized by any write call, but readSide - only
      if buffer is empty. In code fragment below:
      ---PipedInputStream.java---
      1 public synchronized int read() throws IOException {
      2 int trials = 2;
      3 while (in < 0) {
      4 readSide = Thread.currentThread();
      5 if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0))
      I recommend to move line 4 out of the loop, after line 2.

      1 public synchronized int read() throws IOException {
      2 int trials = 2;
      3 readSide = Thread.currentThread();
      4 while (in < 0) {
      5 if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0))

            zlisunw Zhenghua Li (Inactive)
            mgorshen Mikhail Gorshenev (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: