Skip to content

michael-borkowski/java-piped-streams-test

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 

Repository files navigation

java-piped-streams-test

NOTE: The described bug has been reported and its status is Won't Fix. See http://stackoverflow.com/a/28617908/4585628 for more information.

This repository is used to demonstrate a bug in java.io.PipedInputStream (the Oracle Java implementation, present in 1.7 and 1.8). It is a minimal working example to deminstrate that the PipedInputStream contains a bug which causes the corresponding PipedOutputStream to wait up to one second for free space to write to, when actually the space is free sooner. One second might be rather long for some applications.

Problem Description

The basic problem boils down to the following code:

private static void threadA() throws IOException, InterruptedException {
  logA("Filling pipe...");
  pos.write(new byte[5]);
  logA("Pipe full. Writing one more byte...");
  pos.write(0);
  logA("Done.");
}

private static void threadB() throws IOException, InterruptedException {
  logB("Sleeping a bit...");
  Thread.sleep(100);
  logB("Making space in pipe...");
  pis.read();
  logB("Done.");
}

pis and pos are connected PipedInputStream and PipedOutputStream instances, respectively. logA and logB are helper functions which output the thread name (A or B), a timestamp in milliseconds and the message. The output is as follows:

     0 A: Filling pipe...
     6 B: Sleeping a bit...
     7 A: Pipe full. Writing one more byte...
   108 B: Making space in pipe...
   109 B: Done.
  1009 A: Done.

As one can see, there is one second (1000 ms) between B: Done and A: Done. This is caused by the implementation of PipedInputStream in the Oracle Java 1.7.0_67, which is as follows:

private void awaitSpace() throws IOException {
    while (in == out) {
        checkStateForReceive();

        /* full: kick any waiting readers */
        notifyAll();
        try {
            wait(1000);
        } catch (InterruptedException ex) {
            throw new java.io.InterruptedIOException();
        }
    }
}

The wait(1000) is only interrupted by either hitting the timeout (1000 ms, as seen above), or a call to notifyAll(), which only happens in the following cases:

  • In awaitSpace(), before wait(1000), as we can seein the snippet above
  • In receivedLast(), which is called when the stream is closed (not applicable here)
  • In read(), but only if read() is waiting for an empty buffer to fill up -- also not applicable here

Oracle's implementation of the read() method is quite long, I won't include it here, but it can be found on the web (for example here at lines 304 to 342).

Suggested Fix

A suggested patch would be to add a call to notifyAll() in read() immediately before the return statement. I'm not sure how that would affect overall performance, but it fixes the demonstrated issue (awaitSpace() exits immediately instead of waiting up to 1000 ms).

In order to avoid unnecessary overhead using notifyAll(), a boolean flag can be used -- it is set to true whenever a sleep() is occurring, and only if the flag is true, a notifyAll() is issued (resetting the flag to false).

Affected Java Versions

The following versions have been verified as affected.

$ java -version
java version "1.7.0_67"
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)

$ java -version
java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

StackOverflow Question

http://stackoverflow.com/q/28617175/4585628

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages