JDK-4401235 : BufferedInputStream.read blocks when it should return immediately
  • Type: Bug
  • Status: Closed
  • Resolution: Not an Issue
  • Component: core-libs
  • Sub-Component: java.io
  • Priority: P3
  • Affected Version: 1.3.0
  • OS: generic
  • CPU: generic
  • Submit Date: 2001-01-02
  • Updated Date: 2001-05-21
  • Resolved Date: 2001-05-21
Description

Name: boT120536			Date: 01/02/2001


happens in many JREs, including Apple's MRJ 2.2/Mac, and Sun's JRE 1.3/Linux


/* BufferedInputStream blocking problem

Problem:

   BufferedInputStream blocks until enough bytes are available to fill its
   internal buffer, even if fewer bytes have been requested.  In a client/
   server application, this can cause a deadlock: the client waits forever
   for the buffer to be filled, while the server waits forever for the
   client to finish processing the response and issue another request.

The output of this test program should look like this:

    $ java BisBugTest
    read 0x1020304 in 0 milliseconds.
    read 0x1020304 in 1004 milliseconds.
    read 0x1020304 in 1010 milliseconds.
    read 0x1020304 in 1010 milliseconds.
    read 0x1020304 in 1010 milliseconds.
    
Instead, it looks like this:

    $ java BisBugTest
    read 0x1020304 in 515964 milliseconds.
    read 0x1020304 in 0 milliseconds.
    read 0x1020304 in 0 milliseconds.
    read 0x1020304 in 0 milliseconds.
    read 0x1020304 in 0 milliseconds.
    
In this test run, the first read was delayed by 516 seconds (about 9 minutes).
*/

import java.io.*;

public class BisBugTest {

   public static void main (String args []) {
      try {
          // This creates a stream that sends 1,2,3,4; waits 1 second; repeats
          InputStream r = new RepeatStream (new byte [] {1, 2, 3, 4}, 1000);
          DataInputStream d = new DataInputStream (new BufferedInputStream (r));
       // DataInputStream d = new DataInputStream (r); // this way it works OK
          while (true) {
             long start = System.currentTimeMillis ();
             int i = d.readInt ();
             long end = System.currentTimeMillis ();
             System.out.print ("read 0x" + Integer.toHexString (i));
             System.out.println (" in " + (end - start) + " milliseconds.");
          }
      } catch (Exception e) {
          e.printStackTrace ();
      }
   }

   // This input stream emulates a server that sends a series of bytes, waits a
   // while, sends the same series of bytes again, waits again, ad infinitum...
   public static class RepeatStream extends InputStream {
   
      private int sleepMillis;
      private byte contents [];
      private int index = 0;
      
      public RepeatStream (byte contents [], int sleepMillis) {
         this.contents = contents;
         this.sleepMillis = sleepMillis;
      }
      
      public int available () {
         return contents.length - index;
      }
      
      public int read ()
      throws IOException {
         if (index == contents.length) {
            sleep (sleepMillis);
            index = 0;
         }
         return contents [index++] & 0xff;
      }
      
      private static void sleep (int timeout)
      throws IOException {
         try {
            Thread.sleep (timeout);
         } catch (InterruptedException e) {
            throw new InterruptedIOException (e.getMessage ());
         }
      }
   }
}
(Review ID: 114181) 
======================================================================

Comments
WORK AROUND Name: boT120536 Date: 01/02/2001 The work-around I originally sent was to use "new DataInputStream (inputStream)" instead of "new DataInputStream (new BufferedInputStream (inputStream))". Bret O'Neal <###@###.###>, in review #113529, said the problem could be worked around by using a different constructor. Quoted from the review: "You called the wrong constructor for BufferedInputStream. You want BufferedInputStream(r, SIZE) not BufferedInputStream(r)". The review is incorrect. If you make the change that Bret suggested, the program above will still malfunction, unless SIZE is equal to 1 or 2 or 4. It is only a coincidence that 2 and 4 seem to solve the problem in this particular test program; the only value that really solves the problem is 1. Note that setting the buffer size to 1 is equivalent to not using BufferedInputStream at all, which is the same work-around that I suggested in my original bug report two weeks ago. ======================================================================
2004-06-11

PUBLIC COMMENTS Not a bug. Default buffer size for BufferInputStream in about 2000 bytes, therefore to fill the buffer requires lots of time for the InputStream in question. The user should instead use BufferInputStream constructor with buffer size specified explicitely, i.e. BufferInputStream(r, 5). The goal of the BufferInputStream is to minimize read operations for the underlying InputStream by reading information in large chunks. If ones wants to complitely avoid blocking, then BufferInputStream should not be used.
2004-06-10

EVALUATION Not a bug. Default buffer size for BufferInputStream in about 2000 bytes, therefore to fill the buffer requires lots of time for the InputStream in question. The user should instead use BufferInputStream constructor with buffer size specified explicitely, i.e. BufferInputStream(r, 5). The goal of the BufferInputStream is to minimize read operations for the underlying InputStream by reading information in large chunks. If ones wants to complitely avoid blocking, then BufferInputStream should not be used. konstantin.kladko@Eng 2001-05-21
2001-05-21