JDK-4131126 : java.io.Piped*Stream: read throws "Write end dead", but readable data remains
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 1.2.0
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_2.6
  • CPU: sparc
  • Submitted: 1998-04-21
  • Updated: 1999-01-19
  • Resolved: 1999-01-19
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.2.0 1.2beta4Fixed
Related Reports
Relates :  
Relates :  
Description

Name: diC59631			Date: 04/21/98


This occurs in 1.2beta4 build A.

PipedInputStream.read throws IOException saying WriteEndDead
if the writing thread is no longer alive, EVEN if there
are still unread bytes in the buffer written before the writing
thread terminated. This means that these last remaining bytes
cannot be read.

import java.io.*;

public class T421 {
  public static void main(String[] args) {
    PipedChannel p = new PipedChannel();
    Producer w = new Producer(p);
    Consumer c = new Consumer(p);
    new Thread(w).start();
    new Thread(c).start();
  }
}


class Record implements Serializable {
  String name = "My Name";
  String address = "My address";
  int age = 2;
}

class Producer implements Runnable {
  PipedChannel p;
  Producer(PipedChannel pp) { p = pp; }

  public void run() {  
    Record r = new Record();
    try {
      for (int i = 0; i < 1000; ++i) 
        p.put(r);
    }
    catch(Exception ex) {
      ex.printStackTrace();
      return;
    }
  }
}

class Consumer implements Runnable {
  PipedChannel p;
  Consumer(PipedChannel pp) { p = pp; }

  public void run() {  
    try {
      for (int i = 0; i < 1000; ++i) {
        Record r = (Record)(p.take());
        Thread.sleep(2);
      }
    }
    catch(Exception ex) {
      ex.printStackTrace();
      return;
    }
  }
}




class PipedChannel {
  protected ObjectInputStream in_;
  protected ObjectOutputStream out_;
  protected PipedOutputStream outp_;
  protected PipedInputStream inp_;
  
  public PipedChannel() {
    try {
      outp_ = new PipedOutputStream();
      inp_ = new PipedInputStream();
      inp_.connect(outp_);
    }
    catch (IOException ex) {
      ex.printStackTrace();
      throw new Error("Cannot construct Pipe?");
    }
  }

  // needed since constructor can block on reads

  protected synchronized ObjectInputStream in() {
    try {
      if (in_ == null) in_ = new ObjectInputStream(inp_);
      return in_;
    }
    catch (IOException ex) { ex.printStackTrace(); return null; }
  }

  protected synchronized ObjectOutputStream out() {
    try {
      if (out_ == null)  out_ = new ObjectOutputStream(outp_);
      return out_;
    }
    catch (IOException ex) { ex.printStackTrace();  return null; }
  }


  protected void doPut(Object x) throws InterruptedException {
    try {
      out().writeObject(x);
    }
    catch (InterruptedIOException ex) {
      ex.printStackTrace(); 
      throw new InterruptedException();
    }
    catch (IOException ex) {
      ex.printStackTrace();
      throw new InterruptedException();
    }
  }

 protected Object doTake() throws InterruptedException {
    try {
      return in().readObject();
    }
    catch (InterruptedIOException ex) {
      ex.printStackTrace(); 
      throw new InterruptedException();
    }
    catch (IOException ex) {
      ex.printStackTrace();
      throw new InterruptedException();
    }
    catch (ClassNotFoundException ex) {
      ex.printStackTrace();
      throw new InterruptedException();
    }
 }

  public void put(Object x) throws InterruptedException {
    if (x == null) throw new IllegalArgumentException();
    doPut(x);
  }

 public Object take() throws InterruptedException {
   Object x = null;
   x = doTake();
   return x;
 }



}
(Review ID: 28607)
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: generic FIXED IN: 1.2beta4 INTEGRATED IN: 1.2beta4 VERIFIED IN: 1.2beta4
14-06-2004

EVALUATION Not a bug. According to the JLS, read operation in PipedInputStream will throw IOException "If a thread was providing data bytes to the connected piped output stream, but the thread is no longer alive". In a single writer and single reader thread case, when the writer thread dies, to prevent deadlock, a later read will throw IOException no matter there are unread data in the pipe or not. See also bug 1267045. ###@###.### 1998-04-21 On second thought, this is a bug in that what we're doing now is contrary to what we used to do. The spec should be clarified to say that if there's data in the buffer but the writer dies then the reader thread can still read it. If the buffer is empty and the writer is dead, then the reader should get an IOException. -- mr@eng 4/23/1998 Fixed the code. Added checks in the beginning of read to prevent throwing IOException when the writer thread is dead, but there is still data available in the pipe. This way, the reader thread will be able to read all the remaining data in the pipe. ###@###.### 1998-04-27
27-04-1998