-
Bug
-
Resolution: Fixed
-
P4
-
1.0.2
-
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))
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))
- duplicates
-
JDK-1267045 java.io.PipedOutputStream.write(b) does not work if reader thread dies
-
- Closed
-
- relates to
-
JDK-1266819 The java.io.PipedInputStream.read() method works wrong when writer thread dies
-
- Closed
-
-
JDK-4028322 java.io.PipedInputStream doesn't reliably support multiple writers
-
- Closed
-