Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4859836
Votes 5
Synopsis BufferedReader.close() does not interrupt pending readLine()
Category java:classes_io
Reported Against 1.4
Release Fixed
State 11-Closed, Will Not Fix, bug
Priority: 3-Medium
Related Bugs
Submit Date 07-MAY-2003
Description


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) 
======================================================================
Posted Date : 2005-11-03 06:36:10.0
Work Around
N/A
Evaluation
Submitter is correct. BufferedReader has worked this way since inception.
  xxxxx@xxxxx   2003-05-08
The provided sample code would actually hit close synchronization, first in BufferedReader.close() and also in the InputStreamReader.close() implementation.  This synchronization has existed since the class was introduced in jdk1.1.  Therefore, it is probable that large amounts of existing code intentionally or inadverently depend on it. 
 
Closing this bug as "will not fix".
Posted Date : 2005-11-03 06:36:10.0
Comments
  
  Include a link with my name & email   


PLEASE NOTE: JDK6 is formerly known as Project Mustang