United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4401235 : BufferedInputStream.read blocks when it should return immediately

Details
Type:
Bug
Submit Date:
2001-01-02
Status:
Closed
Updated Date:
2001-05-21
Project Name:
JDK
Resolved Date:
2001-05-21
Component:
core-libs
OS:
generic
Sub-Component:
java.io
CPU:
generic
Priority:
P3
Resolution:
Not an Issue
Affected Versions:
1.3.0
Fixed Versions:

Related Reports

Sub Tasks

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



Hardware and Software, Engineered to Work Together