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

Channels.newInputStream() and newOutputStream() synchronize too much

    XMLWordPrintable

Details

    • Bug
    • Resolution: Duplicate
    • P3
    • None
    • 1.4.1
    • core-libs
    • x86
    • windows_nt

    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)
      ======================================================================

      Attachments

        Issue Links

          Activity

            People

              mr Mark Reinhold
              rmandalasunw Ranjith Mandala (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: