title: Did I find a bug in java.io.PipedInputStream?
body: <p>I'm not sure, but I'm pretty certain that I've found a bug (or a non-documented feature) in the Oracle Java implementation (1.7.0_67 and 1.8.0_31 I could verify as affected).</p>
  
  <p><strong>The Symptom</strong></p>
  
  <p>When the pipe is full, a write to the pipe might wait up to one second longer than needed for the pipe to become free again. A minimal example of the problem is as follows (I've pushed the sample shown here to <a href="https://github.com/michael-borkowski/java-piped-streams-test" rel="noreferrer" title="here">a repository on GitHub</a>):</p>
  
  <pre><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.");
  }
  </code></pre>
  
  <p><code>pis</code> and <code>pos</code> are connected <code>PipedInputStream</code> and <code>PipedOutputStream</code> instances, respectively. <code>logA</code> and <code>logB</code> are helper functions which output the thread name (A or B), a timestamp in milliseconds and the message. The output is as follows:</p>
  
  <pre><code>     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.
  </code></pre>
  
  <p>As one can see, there is one second (1000 ms) between <code>B: Done</code> and <code>A: Done</code>. This is caused by the implementation of <code>PipedInputStream</code> in the Oracle Java 1.7.0_67, which is as follows:</p>
  
  <pre><code>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();
          }
      }
  }
  </code></pre>
  
  <p>The <code>wait(1000)</code> is only interrupted by either hitting the timeout (1000 ms, as seen above), or a call to <code>notifyAll()</code>, which only happens in the following cases:</p>
  
  <ul>
  <li>In <code>awaitSpace()</code>, before <code>wait(1000)</code>, as we can seein the snippet above</li>
  <li>In <code>receivedLast()</code>, which is called when the stream is closed (not applicable here)</li>
  <li>In <code>read()</code>, but <strong>only if <code>read()</code> is waiting for an empty buffer to fill up</strong> -- also not applicable here</li>
  </ul>
  
  <p><strong>The Question</strong></p>
  
  <p>Does anyone have enough experience with Java to tell me whether this is supposed to be intended behavior? The method <code>awaitSpace()</code> is used by <code>PipedOutputStream.write(...)</code> to wait for free space, and their contract simply states:</p>
  
  <blockquote>
    <p>This method blocks until all the bytes are written to the output stream.</p>
  </blockquote>
  
  <p>While this is strictly not violated, 1 second of waiting time seems quite long. If I were to fix this (minimize/lower waiting time), I'd suggest inserting a <code>notifyAll()</code> at the end of each read to make sure that a waiting writer gets notified. To avoid additional time overhead for synchronization, a simple boolean flag could be used (and wouldn't hurt thread safety).</p>
  
  <p><strong>Affected Java Versions</strong></p>
  
  <p>So far, I could verify this on Java 7 and Java 8, the following versions to be precise:</p>
  
  <pre><code>$ 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)
  </code></pre>