7184932: Remove the temporary Selector usage in the NIO socket adapters
authorrobm
Thu, 04 Oct 2012 19:53:08 +0100
changeset 14025 fbebe005a3ee
parent 14024 694c379c2958
child 14027 9c48ad0b8972
7184932: Remove the temporary Selector usage in the NIO socket adapters Reviewed-by: alanb
jdk/make/java/nio/mapfile-bsd
jdk/make/java/nio/mapfile-linux
jdk/make/java/nio/mapfile-solaris
jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java
jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java
jdk/src/share/classes/sun/nio/ch/Net.java
jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java
jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java
jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java
jdk/src/share/classes/sun/nio/ch/Util.java
jdk/src/solaris/native/sun/nio/ch/Net.c
jdk/src/windows/native/sun/nio/ch/Net.c
jdk/test/java/nio/channels/etc/AdaptorCloseAndInterrupt.java
--- a/jdk/make/java/nio/mapfile-bsd	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/make/java/nio/mapfile-bsd	Thu Oct 04 19:53:08 2012 +0100
@@ -108,6 +108,7 @@
 		Java_sun_nio_ch_Net_setInterface6;
 		Java_sun_nio_ch_Net_getInterface6;
 		Java_sun_nio_ch_Net_shutdown;
+		Java_sun_nio_ch_Net_poll;
                 Java_sun_nio_ch_PollArrayWrapper_interrupt;
                 Java_sun_nio_ch_PollArrayWrapper_poll0;
                 Java_sun_nio_ch_ServerSocketChannelImpl_accept0;
--- a/jdk/make/java/nio/mapfile-linux	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/make/java/nio/mapfile-linux	Thu Oct 04 19:53:08 2012 +0100
@@ -116,6 +116,7 @@
 		Java_sun_nio_ch_Net_setInterface6;
 		Java_sun_nio_ch_Net_getInterface6;
 		Java_sun_nio_ch_Net_shutdown;
+		Java_sun_nio_ch_Net_poll;
                 Java_sun_nio_ch_PollArrayWrapper_interrupt;
                 Java_sun_nio_ch_PollArrayWrapper_poll0;
                 Java_sun_nio_ch_ServerSocketChannelImpl_accept0;
--- a/jdk/make/java/nio/mapfile-solaris	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/make/java/nio/mapfile-solaris	Thu Oct 04 19:53:08 2012 +0100
@@ -104,6 +104,7 @@
 		Java_sun_nio_ch_Net_setInterface6;
 		Java_sun_nio_ch_Net_getInterface6;
 		Java_sun_nio_ch_Net_shutdown;
+		Java_sun_nio_ch_Net_poll;
                 Java_sun_nio_ch_PollArrayWrapper_interrupt;
                 Java_sun_nio_ch_PollArrayWrapper_poll0;
                 Java_sun_nio_ch_ServerSocketChannelImpl_accept0;
--- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Thu Oct 04 19:53:08 2012 +0100
@@ -1059,6 +1059,28 @@
         return translateReadyOps(ops, 0, sk);
     }
 
+    // package-private
+    int poll(int events, long timeout) throws IOException {
+        assert Thread.holdsLock(blockingLock()) && !isBlocking();
+
+        synchronized (readLock) {
+            int n = 0;
+            try {
+                begin();
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        return 0;
+                    readerThread = NativeThread.current();
+                }
+                n = Net.poll(fd, events, timeout);
+            } finally {
+                readerThread = 0;
+                end(n > 0);
+            }
+            return n;
+        }
+    }
+
     /**
      * Translates an interest operation set into a native poll event set
      */
--- a/jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Thu Oct 04 19:53:08 2012 +0100
@@ -176,40 +176,31 @@
             return dc.receive(bb);
         }
 
-        // Implement timeout with a selector
-        SelectionKey sk = null;
-        Selector sel = null;
         dc.configureBlocking(false);
         try {
             int n;
             SocketAddress sender;
             if ((sender = dc.receive(bb)) != null)
                 return sender;
-            sel = Util.getTemporarySelector(dc);
-            sk = dc.register(sel, SelectionKey.OP_READ);
             long to = timeout;
             for (;;) {
                 if (!dc.isOpen())
                      throw new ClosedChannelException();
                 long st = System.currentTimeMillis();
-                int ns = sel.select(to);
-                if (ns > 0 && sk.isReadable()) {
+                int result = dc.poll(PollArrayWrapper.POLLIN, to);
+                if (result > 0 &&
+                        ((result & PollArrayWrapper.POLLIN) != 0)) {
                     if ((sender = dc.receive(bb)) != null)
                         return sender;
                 }
-                sel.selectedKeys().remove(sk);
                 to -= System.currentTimeMillis() - st;
                 if (to <= 0)
                     throw new SocketTimeoutException();
 
             }
         } finally {
-            if (sk != null)
-                sk.cancel();
             if (dc.isOpen())
                 dc.configureBlocking(true);
-            if (sel != null)
-                Util.releaseTemporarySelector(sel);
         }
     }
 
--- a/jdk/src/share/classes/sun/nio/ch/Net.java	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/Net.java	Thu Oct 04 19:53:08 2012 +0100
@@ -409,6 +409,9 @@
                                              int level, int opt, int arg)
         throws IOException;
 
+    static native int poll(FileDescriptor fd, int events, long timeout)
+        throws IOException;
+
     // -- Multicast support --
 
 
--- a/jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java	Thu Oct 04 19:53:08 2012 +0100
@@ -102,37 +102,26 @@
                     return sc.socket();
                 }
 
-                // Implement timeout with a selector
-                SelectionKey sk = null;
-                Selector sel = null;
                 ssc.configureBlocking(false);
                 try {
                     SocketChannel sc;
                     if ((sc = ssc.accept()) != null)
                         return sc.socket();
-                    sel = Util.getTemporarySelector(ssc);
-                    sk = ssc.register(sel, SelectionKey.OP_ACCEPT);
                     long to = timeout;
                     for (;;) {
                         if (!ssc.isOpen())
                             throw new ClosedChannelException();
                         long st = System.currentTimeMillis();
-                        int ns = sel.select(to);
-                        if (ns > 0 &&
-                            sk.isAcceptable() && ((sc = ssc.accept()) != null))
+                        int result = ssc.poll(PollArrayWrapper.POLLIN, to);
+                        if (result > 0 && ((sc = ssc.accept()) != null))
                             return sc.socket();
-                        sel.selectedKeys().remove(sk);
                         to -= System.currentTimeMillis() - st;
                         if (to <= 0)
                             throw new SocketTimeoutException();
                     }
                 } finally {
-                    if (sk != null)
-                        sk.cancel();
                     if (ssc.isOpen())
                         ssc.configureBlocking(true);
-                    if (sel != null)
-                        Util.releaseTemporarySelector(sel);
                 }
 
             } catch (Exception x) {
--- a/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Thu Oct 04 19:53:08 2012 +0100
@@ -324,6 +324,28 @@
         return translateReadyOps(ops, 0, sk);
     }
 
+    // package-private
+    int poll(int events, long timeout) throws IOException {
+        assert Thread.holdsLock(blockingLock()) && !isBlocking();
+
+        synchronized (lock) {
+            int n = 0;
+            try {
+                begin();
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        return 0;
+                    thread = NativeThread.current();
+                }
+                n = Net.poll(fd, events, timeout);
+            } finally {
+                thread = 0;
+                end(n > 0);
+            }
+            return n;
+        }
+    }
+
     /**
      * Translates an interest operation set into a native poll event set
      */
--- a/jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/SocketAdaptor.java	Thu Oct 04 19:53:08 2012 +0100
@@ -97,25 +97,19 @@
                     return;
                 }
 
-                // Implement timeout with a selector
-                SelectionKey sk = null;
-                Selector sel = null;
                 sc.configureBlocking(false);
                 try {
                     if (sc.connect(remote))
                         return;
-                    sel = Util.getTemporarySelector(sc);
-                    sk = sc.register(sel, SelectionKey.OP_CONNECT);
                     long to = timeout;
                     for (;;) {
                         if (!sc.isOpen())
                             throw new ClosedChannelException();
                         long st = System.currentTimeMillis();
-                        int ns = sel.select(to);
-                        if (ns > 0 &&
-                            sk.isConnectable() && sc.finishConnect())
+
+                        int result = sc.poll(PollArrayWrapper.POLLCONN, to);
+                        if (result > 0 && sc.finishConnect())
                             break;
-                        sel.selectedKeys().remove(sk);
                         to -= System.currentTimeMillis() - st;
                         if (to <= 0) {
                             try {
@@ -125,12 +119,8 @@
                         }
                     }
                 } finally {
-                    if (sk != null)
-                        sk.cancel();
                     if (sc.isOpen())
                         sc.configureBlocking(true);
-                    if (sel != null)
-                        Util.releaseTemporarySelector(sel);
                 }
 
             } catch (Exception x) {
@@ -199,39 +189,29 @@
                     throw new IllegalBlockingModeException();
                 if (timeout == 0)
                     return sc.read(bb);
+                sc.configureBlocking(false);
 
-                // Implement timeout with a selector
-                SelectionKey sk = null;
-                Selector sel = null;
-                sc.configureBlocking(false);
                 try {
                     int n;
                     if ((n = sc.read(bb)) != 0)
                         return n;
-                    sel = Util.getTemporarySelector(sc);
-                    sk = sc.register(sel, SelectionKey.OP_READ);
                     long to = timeout;
                     for (;;) {
                         if (!sc.isOpen())
                             throw new ClosedChannelException();
                         long st = System.currentTimeMillis();
-                        int ns = sel.select(to);
-                        if (ns > 0 && sk.isReadable()) {
+                        int result = sc.poll(PollArrayWrapper.POLLIN, to);
+                        if (result > 0) {
                             if ((n = sc.read(bb)) != 0)
                                 return n;
                         }
-                        sel.selectedKeys().remove(sk);
                         to -= System.currentTimeMillis() - st;
                         if (to <= 0)
                             throw new SocketTimeoutException();
                     }
                 } finally {
-                    if (sk != null)
-                        sk.cancel();
                     if (sc.isOpen())
                         sc.configureBlocking(true);
-                    if (sel != null)
-                        Util.releaseTemporarySelector(sel);
                 }
 
             }
--- a/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java	Thu Oct 04 19:53:08 2012 +0100
@@ -914,6 +914,28 @@
         return translateReadyOps(ops, 0, sk);
     }
 
+    // package-private
+    int poll(int events, long timeout) throws IOException {
+        assert Thread.holdsLock(blockingLock()) && !isBlocking();
+
+        synchronized (readLock) {
+            int n = 0;
+            try {
+                begin();
+                synchronized (stateLock) {
+                    if (!isOpen())
+                        return 0;
+                    readerThread = NativeThread.current();
+                }
+                n = Net.poll(fd, events, timeout);
+            } finally {
+                readerCleanup();
+                end(n > 0);
+            }
+            return n;
+        }
+    }
+
     /**
      * Translates an interest operation set into a native poll event set
      */
--- a/jdk/src/share/classes/sun/nio/ch/Util.java	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/Util.java	Thu Oct 04 19:53:08 2012 +0100
@@ -218,66 +218,6 @@
         ((DirectBuffer)buf).cleaner().clean();
     }
 
-    private static class SelectorWrapper {
-        private Selector sel;
-        private SelectorWrapper (Selector sel) {
-            this.sel = sel;
-            Cleaner.create(this, new Closer(sel));
-        }
-        private static class Closer implements Runnable {
-            private Selector sel;
-            private Closer (Selector sel) {
-                this.sel = sel;
-            }
-            public void run () {
-                try {
-                    sel.close();
-                } catch (Throwable th) {
-                    throw new Error(th);
-                }
-            }
-        }
-        public Selector get() { return sel;}
-    }
-
-    // Per-thread cached selector
-    private static ThreadLocal<SoftReference<SelectorWrapper>> localSelector
-        = new ThreadLocal<SoftReference<SelectorWrapper>>();
-    // Hold a reference to the selWrapper object to prevent it from
-    // being cleaned when the temporary selector wrapped is on lease.
-    private static ThreadLocal<SelectorWrapper> localSelectorWrapper
-        = new ThreadLocal<SelectorWrapper>();
-
-    // When finished, invoker must ensure that selector is empty
-    // by cancelling any related keys and explicitly releasing
-    // the selector by invoking releaseTemporarySelector()
-    static Selector getTemporarySelector(SelectableChannel sc)
-        throws IOException
-    {
-        SoftReference<SelectorWrapper> ref = localSelector.get();
-        SelectorWrapper selWrapper = null;
-        Selector sel = null;
-        if (ref == null
-            || ((selWrapper = ref.get()) == null)
-            || ((sel = selWrapper.get()) == null)
-            || (sel.provider() != sc.provider())) {
-            sel = sc.provider().openSelector();
-            selWrapper = new SelectorWrapper(sel);
-            localSelector.set(new SoftReference<SelectorWrapper>(selWrapper));
-        }
-        localSelectorWrapper.set(selWrapper);
-        return sel;
-    }
-
-    static void releaseTemporarySelector(Selector sel)
-        throws IOException
-    {
-        // Selector should be empty
-        sel.selectNow();                // Flush cancelled keys
-        assert sel.keys().isEmpty() : "Temporary selector not empty";
-        localSelectorWrapper.set(null);
-    }
-
 
     // -- Random stuff --
 
--- a/jdk/src/solaris/native/sun/nio/ch/Net.c	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/solaris/native/sun/nio/ch/Net.c	Thu Oct 04 19:53:08 2012 +0100
@@ -38,6 +38,7 @@
 #include "net_util_md.h"
 #include "nio_util.h"
 #include "nio.h"
+#include "sun_nio_ch_PollArrayWrapper.h"
 
 #ifdef _ALLBSD_SOURCE
 
@@ -627,6 +628,26 @@
         handleSocketError(env, errno);
 }
 
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_Net_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout)
+{
+    struct pollfd pfd;
+    int rv;
+    pfd.fd = fdval(env, fdo);
+    pfd.events = events;
+    rv = poll(&pfd, 1, timeout);
+
+    if (rv >= 0) {
+        return pfd.revents;
+    } else if (errno == EINTR) {
+        return IOS_INTERRUPTED;
+    } else if (rv < 0) {
+        handleSocketError(env, errno);
+        return IOS_THROWN;
+    }
+}
+
+
 /* Declared in nio_util.h */
 
 jint
--- a/jdk/src/windows/native/sun/nio/ch/Net.c	Thu Oct 04 10:04:56 2012 -0700
+++ b/jdk/src/windows/native/sun/nio/ch/Net.c	Thu Oct 04 19:53:08 2012 +0100
@@ -35,6 +35,7 @@
 #include "net_util.h"
 
 #include "sun_nio_ch_Net.h"
+#include "sun_nio_ch_PollArrayWrapper.h"
 
 /**
  * Definitions to allow for building with older SDK include files.
@@ -524,3 +525,49 @@
         NET_ThrowNew(env, WSAGetLastError(), "shutdown");
     }
 }
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_Net_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout)
+{
+    int rv;
+    int revents = 0;
+    struct timeval t;
+    int lastError = 0;
+    fd_set rd, wr, ex;
+    jint fd = fdval(env, fdo);
+
+    t.tv_sec = timeout / 1000;
+    t.tv_usec = (timeout % 1000) * 1000;
+
+    FD_ZERO(&rd);
+    FD_ZERO(&wr);
+    FD_ZERO(&ex);
+    if (events & sun_nio_ch_PollArrayWrapper_POLLIN) {
+        FD_SET(fd, &rd);
+    }
+    if (events & sun_nio_ch_PollArrayWrapper_POLLOUT ||
+        events & sun_nio_ch_PollArrayWrapper_POLLCONN) {
+        FD_SET(fd, &wr);
+    }
+    FD_SET(fd, &ex);
+
+    rv = select(fd+1, &rd, &wr, &ex, &t);
+
+    /* save last winsock error */
+    if (rv == SOCKET_ERROR) {
+        handleSocketError(env, lastError);
+        return IOS_THROWN;
+    } else if (rv >= 0) {
+        rv = 0;
+        if (FD_ISSET(fd, &rd)) {
+            rv |= sun_nio_ch_PollArrayWrapper_POLLIN;
+        }
+        if (FD_ISSET(fd, &wr)) {
+            rv |= sun_nio_ch_PollArrayWrapper_POLLOUT;
+        }
+        if (FD_ISSET(fd, &ex)) {
+            rv |= sun_nio_ch_PollArrayWrapper_POLLERR;
+        }
+    }
+    return rv;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/channels/etc/AdaptorCloseAndInterrupt.java	Thu Oct 04 19:53:08 2012 +0100
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 7184932
+ * @summary Test asynchronous close and interrupt of timed socket adapter methods
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.channels.spi.AbstractSelectableChannel;
+import java.net.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.Random;
+
+
+public class AdaptorCloseAndInterrupt {
+    private static final ScheduledExecutorService pool =
+        Executors.newScheduledThreadPool(1);
+    final ServerSocketChannel listener;
+    final DatagramChannel peer;
+    final int port;
+
+    public AdaptorCloseAndInterrupt() {
+        listener = null;
+        peer = null;
+        port = -1;
+    }
+
+    public AdaptorCloseAndInterrupt(ServerSocketChannel listener) {
+        this.listener = listener;
+        this.port = listener.socket().getLocalPort();
+        this.peer = null;
+    }
+
+    public AdaptorCloseAndInterrupt(DatagramChannel listener) {
+        this.peer = listener;
+        this.port = peer.socket().getLocalPort();
+        this.listener = null;
+    }
+
+    public static void main(String args[]) throws Exception {
+        try {
+            try (ServerSocketChannel listener = ServerSocketChannel.open()) {
+                listener.socket().bind(null);
+                new AdaptorCloseAndInterrupt(listener).scReadAsyncClose();
+                new AdaptorCloseAndInterrupt(listener).scReadAsyncInterrupt();
+            }
+
+            try (DatagramChannel peer = DatagramChannel.open()) {
+                peer.socket().bind(null);
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose();
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt();
+            }
+
+            new AdaptorCloseAndInterrupt().ssAcceptAsyncClose();
+            new AdaptorCloseAndInterrupt().ssAcceptAsyncInterrupt();
+        } finally {
+            pool.shutdown();
+        }
+        System.out.println("Test Passed");
+    }
+
+    void scReadAsyncClose() throws IOException {
+        try {
+            SocketChannel sc = SocketChannel.open(new InetSocketAddress(
+                "127.0.0.1", port));
+            sc.socket().setSoTimeout(30*1000);
+
+            doAsyncClose(sc);
+
+            try {
+                sc.socket().getInputStream().read(new byte[100]);
+                throw new RuntimeException("read should not have completed");
+            } catch (ClosedChannelException expected) {}
+
+            if (!sc.socket().isClosed())
+                throw new RuntimeException("socket is not closed");
+        } finally {
+            // accept connection and close it.
+            listener.accept().close();
+        }
+    }
+
+    void scReadAsyncInterrupt() throws IOException {
+        try {
+            final SocketChannel sc = SocketChannel.open(new InetSocketAddress(
+                "127.0.0.1", port));
+            sc.socket().setSoTimeout(30*1000);
+
+            doAsyncInterrupt();
+
+            try {
+                sc.socket().getInputStream().read(new byte[100]);
+                throw new RuntimeException("read should not have completed");
+            } catch (ClosedByInterruptException expected) {
+                Thread.currentThread().interrupted();
+            }
+
+            if (!sc.socket().isClosed())
+                throw new RuntimeException("socket is not closed");
+        } finally {
+            // accept connection and close it.
+            listener.accept().close();
+        }
+    }
+
+    void dcReceiveAsyncClose() throws IOException {
+        DatagramChannel dc = DatagramChannel.open();
+        dc.connect(new InetSocketAddress(
+            "127.0.0.1", port));
+        dc.socket().setSoTimeout(30*1000);
+
+        doAsyncClose(dc);
+
+        try {
+            dc.socket().receive(new DatagramPacket(new byte[100], 100));
+            throw new RuntimeException("receive should not have completed");
+        } catch (ClosedChannelException expected) {}
+
+        if (!dc.socket().isClosed())
+            throw new RuntimeException("socket is not closed");
+    }
+
+    void dcReceiveAsyncInterrupt() throws IOException {
+        DatagramChannel dc = DatagramChannel.open();
+        dc.connect(new InetSocketAddress(
+            "127.0.0.1", port));
+        dc.socket().setSoTimeout(30*1000);
+
+        doAsyncInterrupt();
+
+        try {
+            dc.socket().receive(new DatagramPacket(new byte[100], 100));
+            throw new RuntimeException("receive should not have completed");
+        } catch (ClosedByInterruptException expected) {
+            Thread.currentThread().interrupted();
+        }
+
+        if (!dc.socket().isClosed())
+            throw new RuntimeException("socket is not closed");
+    }
+
+    void ssAcceptAsyncClose() throws IOException {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.socket().bind(null);
+        ssc.socket().setSoTimeout(30*1000);
+
+        doAsyncClose(ssc);
+
+        try {
+            ssc.socket().accept();
+            throw new RuntimeException("accept should not have completed");
+        } catch (ClosedChannelException expected) {}
+
+        if (!ssc.socket().isClosed())
+            throw new RuntimeException("socket is not closed");
+    }
+
+    void ssAcceptAsyncInterrupt() throws IOException {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.socket().bind(null);
+        ssc.socket().setSoTimeout(30*1000);
+
+        doAsyncInterrupt();
+
+        try {
+            ssc.socket().accept();
+            throw new RuntimeException("accept should not have completed");
+        } catch (ClosedByInterruptException expected) {
+            Thread.currentThread().interrupted();
+        }
+
+        if (!ssc.socket().isClosed())
+            throw new RuntimeException("socket is not closed");
+    }
+
+    void doAsyncClose(final AbstractSelectableChannel sc) {
+        AdaptorCloseAndInterrupt.pool.schedule(new Callable<Void>() {
+            public Void call() throws Exception {
+                sc.close();
+                return null;
+            }
+        }, new Random().nextInt(1000), TimeUnit.MILLISECONDS);
+    }
+
+    void doAsyncInterrupt() {
+        final Thread current = Thread.currentThread();
+        AdaptorCloseAndInterrupt.pool.schedule(new Callable<Void>() {
+            public Void call() throws Exception {
+                current.interrupt();
+                return null;
+            }
+        }, new Random().nextInt(1000), TimeUnit.MILLISECONDS);
+    }
+
+}