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

(so) SocketChannel.close doesn't preempt socket adapter emulation of timed reads

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P4
    • Resolution: Duplicate
    • Affects Version/s: 6
    • Fix Version/s: tbd_minor
    • Component/s: core-libs
    • Labels:
    • Subcomponent:
    • Introduced In Version:
    • CPU:
      x86
    • OS:
      solaris_2.5.1

      Description

      FULL PRODUCT VERSION :
      Java(TM) SE Runtime Environment (build 1.6.0-b105)
      Java HotSpot(TM) 64-Bit Server VM (build 1.6.0-b105, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Linux version 2.6.13-15.8-amd64 (geeko@buildhost) (gcc version 3.3.5 20050117 (prerelease) (SUSE Linux)) #4 SMP Fri Mar 3 20:18:21 CET 2006

      A DESCRIPTION OF THE PROBLEM :
      I encountered that an asynchronous close on a SocketChannel which is configured to be blocking and has an SO_TIMEOUT will not behave as expected. I think it should exactly imitate a socket's behavior. The used socket adapter emulates timeouts by putting the socket channel into non-blocking mode and using a temporary Selector to poll the socket as far as I found out. The asynchronous close of the socket channel (or socket) doesn't know about the timed operation and so doesn't wakeup the Selector. Instead of this it hangs until the timeout run out at this stacktrace:

      at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
      at sun.nio.ch.EPollArrayWrapper.poll(Unknown Source)
      at sun.nio.ch.EPollSelectorImpl.doSelect(Unknown Source)
      at sun.nio.ch.SelectorImpl.lockAndDoSelect(Unknown Source)
      at sun.nio.ch.SelectorImpl.select(Unknown Source)
      at sun.nio.ch.SocketAdaptor$SocketInputStream.read(Unknown Source)
      at sun.nio.ch.ChannelInputStream.read(Unknown Source)

      On Windows platform it behaves as expected.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      run the following Unittest

      1. accept a socketchannel via an serversocketchannel
      2. configure it to blocking
      3. set an so timeout on it
      4. do an asynchronous close on that socketchannel

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The Reader thread should get an ClosedChannelException in read() after about 1500 ms (the time, close() is called).
      ACTUAL -
      We get an SocketTimeoutException after SO_TIMEOUT which is set to 10000

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.IOException;
      import java.io.InputStream;
      import java.net.InetAddress;
      import java.net.InetSocketAddress;
      import java.net.Socket;
      import java.nio.channels.ClosedChannelException;
      import java.nio.channels.ServerSocketChannel;
      import java.nio.channels.SocketChannel;

      import junit.framework.Assert;
      import junit.framework.TestCase;



      /**
       * @author ###@###.###
       */
      public class AsyncCloseNIOChannelTest extends TestCase {

          private class Acceptor extends Thread {

              private ServerSocketChannel m_server;

              Acceptor() throws IOException {
                  super.setDaemon(true);
                  this.m_server = ServerSocketChannel.open();
                  this.m_server.socket().bind(AsyncCloseNIOChannelTest.this.m_serverAddress);
              }
              
              /**
               * @see java.lang.Thread#run()
               */
              @Override
              public void run() {
                  try {
                      AsyncCloseNIOChannelTest.this.m_acceptedChannel = this.m_server.accept();
                      AsyncCloseNIOChannelTest.this.m_acceptedChannel.configureBlocking(true);
                  } catch (IOException e) {
                      Assert.fail("io error in accept: " + e + ", " + e.getMessage());
                  }
              }
              
          }

          
          private class Reader extends Thread {

              /**
               * @see java.lang.Thread#run()
               */
              @Override
              public void run() {
                  long start = System.currentTimeMillis();
                  try {
                      AsyncCloseNIOChannelTest.this.m_acceptedChannel.socket().setSoTimeout(10000);
                      InputStream inputStream = AsyncCloseNIOChannelTest.this.m_acceptedChannel.socket().getInputStream();
                      inputStream.read();
                  } catch (ClosedChannelException e) {
                      // this is what we wan't
                      e.printStackTrace(System.out);
                  } catch (IOException e) {
                      Assert.fail("io error in read: " + e + ", " + e.getMessage());
                  } finally {
                      AsyncCloseNIOChannelTest.this.m_duration = (System.currentTimeMillis() - start);
                  }
              }
              
          }
          
          long m_duration;
          InetSocketAddress m_serverAddress;
          SocketChannel m_acceptedChannel;

          /**
           * @see junit.framework.TestCase#setUp()
           */
          protected void setUp() throws Exception {
              super.setUp();
              this.m_acceptedChannel = null;
              this.m_duration = Long.MAX_VALUE;
              this.m_serverAddress = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 17007);
          }

          
          /**
           * @throws Exception
           */
          public void testClose() throws Exception {
                  
              new Acceptor().start();
              
              Socket socket = new Socket();
              socket.connect(this.m_serverAddress);
              
              Reader reader = new Reader();
              reader.setName("Reader");
              reader.start();
              
              int sleep = 1500;
              
              Thread.sleep(sleep);
              
              //new StackDump(reader).printStackTrace(System.out);
              this.m_acceptedChannel.close();
              
              reader.join();
              
              assertTrue("close did not interrupt read after about 2500 ms, but: " + this.m_duration , this.m_duration < (sleep * 2));
          }
      }
      ---------- END SOURCE ----------

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                robm Robert Mckenna
                Reporter:
                ryeung Roger Yeung (Inactive)
              • Votes:
                0 Vote for this issue
                Watchers:
                0 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  Imported:
                  Indexed: