JDK-4774871 : Channels.newInputStream() and newOutputStream() synchronize too much
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 1.4.1
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_nt
  • CPU: x86
  • Submitted: 2002-11-06
  • Updated: 2002-11-14
  • Resolved: 2002-11-14
Related Reports
Duplicate :  
Description

Name: rmT116609			Date: 11/06/2002


FULL PRODUCT VERSION :
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)

FULL OPERATING SYSTEM VERSION : Windows NT Version 4.0SP 6a

A DESCRIPTION OF THE PROBLEM :
The InputStream and OutputStream returned by Channels.newInputStream() and Channels.newOutputStream() perform unnecessary synchronization on reading and writing to the underlying channels.

With SocketChannel for example, this can cause a deadlock if one thread is blocked on reading the channel and another thread is trying to write to it. If the read and write were done directly to the SocketChannel, they would not block
each other. When done through the streams from Channels, they unnecessarily block each other.

As the specification for ReadableByteChannel and WritableByteChannel clearly state, the channels perform the necessary synchronization on reads and writes.

Apparently, at least the output stream returned by newOutputStream() synchronizes the write on the blocking lock of the channel, if it's a SelectableChannel. Presumably the input stream does the same (since a deadlock
is observed). This is unnecessary - the streams should not synchronize on the channel's blocking lock as the necessary synchronization is performed by the channel itself.

If you look at the thread dump while the application is hanging, one thread is waiting on a monitor while apparently another thread owns it, and is blocked on the channel (deadlock).

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile the provided source code
2. Run the program: java SocketChannelTest
3. Observe the program's behavior

EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected: The program runs for two seconds and then exits.
Actual: The program hangs in a deadlock.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.SocketChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.ByteChannel;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

public class SocketChannelTest
{
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 9999;
    private static final int LENGTH = 1;

    private static ByteChannel wrapChannel(final ByteChannel channel)
    {
        return channel;
    }

    public static void main(String[] args)
        throws IOException, InterruptedException
    {
        final InetSocketAddress address = new InetSocketAddress(HOST, PORT);

        new Thread("Server thread")
        {
            public void run()
            {
                try
                {
                    ServerSocketChannel serverSocketChannel =
ServerSocketChannel.open();
                    ServerSocket serverSocket = serverSocketChannel.socket();
                    serverSocket.bind(address);
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    InputStream in = Channels.newInputStream(socketChannel);
                    byte[] buffer = new byte[LENGTH];
                    in.read(buffer);
                    socketChannel.close();
                    serverSocketChannel.close();
                }
                catch (IOException ioe)
                {
                    throw new RuntimeException(ioe);
                }
            }
        }.start();

        SocketChannel socketChannel = SocketChannel.open(address);
        final ByteChannel byteChannel = wrapChannel(socketChannel);

        new Thread("Reading client thread")
        {
            public void run()
            {
                try
                {
                    InputStream in = Channels.newInputStream(byteChannel);
                    byte[] buffer = new byte[LENGTH];
                    in.read(buffer);
                }
                catch (ClosedChannelException cce)
                {
                    // Ignore - channel closed by another thread
                }
                catch (IOException ioe)
                {
                    throw new RuntimeException(ioe);
                }
            }
        }.start();

        // Wait for both of the above threads to be running
        Thread.sleep(2000);

        Thread.currentThread().setName("Writing client thread");
        OutputStream out = Channels.newOutputStream(byteChannel);
        byte[] buffer = new byte[LENGTH];
        out.write(buffer);

        byteChannel.close();
    }
}

---------- END SOURCE ----------

CUSTOMER WORKAROUND :
The above program works if you change the wrapChannel()
method to wrap the SocketChannel to a dummy channel that
only forwards the calls to it. It works because it is not
an instance of SelectableChannel:

    private static ByteChannel wrapChannel(final
ByteChannel channel)
    {
        return new ByteChannel()
        {
            public int write(ByteBuffer src)
                throws IOException
            {
                return channel.write(src);
            }

            public int read(ByteBuffer dst)
                throws IOException
            {
                return channel.read(dst);
            }

            public boolean isOpen()
            {
                return channel.isOpen();
            }

            public void close()
                throws IOException
            {
                channel.close();
            }
        };
    }
(Review ID: 166711) 
======================================================================