Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4859836

BufferedReader.close() does not interrupt pending readLine()

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Won't Fix
    • Icon: P3 P3
    • None
    • 1.4.0
    • core-libs
    • x86
    • linux

      Name: nt126004 Date: 05/07/2003


      FULL PRODUCT VERSION :
      Java HotSpot(TM) Client VM (build 1.4.0_03-b04, mixed mode)

      A DESCRIPTION OF THE PROBLEM :
      BufferedReader methods are fully synchronized including the close method. This makes it impossible to close a stream while an I/O read is occurring on another thread. It is often important to issue the close in order to get a read to abort. Example:

      Reader r = new BufferedReader(new FileReader("some I/O device"));
      Thread1:
         String s = r.readLine(); // hangs waiting for data: method is synchronized

      Thread2:
         r.close(); // hangs in synchronization, cannot close stream and wakeup reader

      I realize that nio is better suited for these cases, and
      nio is fine, but there are many cases where converting to nio is not
      practical. There is much legacy code that uses InputStreams and Readers.
      Sometimes you've got your hands on a Reader or InputStream and you need to
      close it to unwedge another thread blocked on I/O. If it happens to be a
      BufferedReader, you're stuck given the current implementation.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      I would expect the close to complete and the readLine to abort.
      The close hangs and the readLine never aborts.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      Test submitted hangs when using BufferedReader.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.*;
      import java.net.*;

      /**
       * This class demonstrates the inability to close a BufferedReader while the
       * Reader is waiting in an I/O reader. For comparison, the test also can
       * be run with a DataInputStream which exhibits the proper close behavior.
       *
       * Run test with "java CloseTest br" (test will hang).
       * Works with DataInputStream, run test with "java CloseTest dis"
       * Set PORT to a free port.
       * Test under java 1.4.
       */
      public class CloseTest
      {
          private static boolean done = false;
          private static int PORT = 9099;

          /**
           * Return a "closeable" InputStream. Uses a connected server socket.
           */
          private static InputStream getInputStream()
          throws Exception
          {
              ServerSocket ss = new ServerSocket(PORT);
              new Thread (new Runnable() {
                  public void run() {
                      try {
                          Thread.sleep(2000);
                          InetAddress host = InetAddress.getLocalHost();
                          Socket s = new Socket(host, PORT);
                          synchronized (this) {
                              while (!done)
                                  wait(1000);
                          }
                      } catch (Exception e) {
                          System.out.println ("Caught " + e.getMessage());
                      }
                  }
              }).start();

              Socket s = ss.accept();
              return s.getInputStream();
          }

          /**
           * Test a BufferedReader. Creates a thread to close the Reader
           * asynchronously. We should abort after the close, but do not due
           * to locking in BufferedReader.
           */
          public static void testBufferedReader()
          {
              try {
                  InputStream sockIn = getInputStream();
                  InputStreamReader isr = new InputStreamReader(sockIn);
                  BufferedReader reader = new BufferedReader(isr);
                  startCloser(reader);

                  System.out.println ("Reader reading ...");
                  String s = reader.readLine();
                  // should never get here
                  System.out.println ("Reader returned " + s);
              } catch (Exception e) {
                  // This is the behavior we want, but never get here
                  System.out.println ("Reader aborted with " + e.getMessage());
              }
              done = true;
          }

          private static void startCloser(final Reader reader)
          {
              new Thread(new Runnable() {
                  public void run() {
                      try {
                          System.out.println ("Closer started ... ");
                          Thread.sleep(5000); // give reader time to start
                          System.out.println ("Closing reader ...");
                          reader.close();
                          System.out.println ("Closing reader complete");
                      } catch (Exception e){}
                  }
              }, "Reader close thread").start();
          }

          /**
           * Test a DataInputStream. Creates a thread to close the InputStream
           * asynchronously. We should abort after the close.
           */
          public static void testDataInputStream()
          {
              try {
                  InputStream sockIn = getInputStream();
                  DataInputStream in = new DataInputStream(sockIn);
                  startCloser(in);

                  System.out.println ("InputStream reading ...");
                  String s = in.readLine();
                  // should never get here
                  System.out.println ("InputStream returned " + s);
              } catch (Exception e) {
                  // This is the behavior we want and it works!!!
                  System.out.println ("InputStream aborted with " + e.getMessage());
              }
              done = true;
          }

          private static void startCloser(final InputStream in)
          {
              new Thread(new Runnable() {
                  public void run() {
                      try {
                          System.out.println ("Closer started ... ");
                          Thread.sleep(5000); // give reader time to start
                          System.out.println ("Closing Inputstream ...");
                          in.close();
                          System.out.println ("Closing InputStream complete");
                      } catch (Exception e){}
                  }
              }, "InputStream close thread").start();
          }

          public static void main(String[] args)
          {
              if (args.length != 1)
                  usage();
              if (args[0].equals("br"))
                  testBufferedReader();
              else
              if (args[0].equals("dis"))
                  testDataInputStream();
              else
                  usage();
          }

          private static void usage()
          {
              System.out.println ("Usage: java CloseTest [br|dis]");
              System.exit(1);
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      It is possible to use DataInputStream rather than BufferedReader, though the readLine method of DataInputStream is deprecated. Also, it may work to close the underlying stream that the BufferedReader is built upon, but that may break encapsulation if the underlying stream should not be exposed.
      (Review ID: 185081)
      ======================================================================

            iris Iris Clark
            nthompsosunw Nathanael Thompson (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: