8232673: (dc) DatagramChannel socket adaptor issues
authoralanb
Sat, 02 Nov 2019 10:02:18 +0000
changeset 58899 5573a7098439
parent 58898 4ec9fc2b2f0d
child 58900 434329f6f456
8232673: (dc) DatagramChannel socket adaptor issues Reviewed-by: dfuchs, chegar
src/java.base/share/classes/java/net/DatagramSocket.java
src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java
src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java
test/jdk/java/nio/channels/DatagramChannel/AdaptDatagramSocket.java
test/jdk/java/nio/channels/DatagramChannel/AdaptorBasic.java
test/jdk/java/nio/channels/DatagramChannel/AdaptorConcurrentIO.java
test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java
test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java
test/jdk/java/nio/channels/etc/AdaptorCloseAndInterrupt.java
--- a/src/java.base/share/classes/java/net/DatagramSocket.java	Fri Nov 01 16:21:17 2019 -0400
+++ b/src/java.base/share/classes/java/net/DatagramSocket.java	Sat Nov 02 10:02:18 2019 +0000
@@ -606,7 +606,6 @@
      * @see #bind(SocketAddress)
      * @since 1.4
      */
-
     public SocketAddress getLocalSocketAddress() {
         if (isClosed())
             return null;
@@ -853,7 +852,7 @@
     public InetAddress getLocalAddress() {
         if (isClosed())
             return null;
-        InetAddress in = null;
+        InetAddress in;
         try {
             in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR);
             if (in.isAnyLocalAddress()) {
@@ -874,8 +873,8 @@
      * is bound.
      *
      * @return  the port number on the local host to which this socket is bound,
-                {@code -1} if the socket is closed, or
-                {@code 0} if it is not bound yet.
+     *          {@code -1} if the socket is closed, or
+     *          {@code 0} if it is not bound yet.
      */
     public int getLocalPort() {
         if (isClosed())
@@ -887,15 +886,16 @@
         }
     }
 
-    /** Enable/disable SO_TIMEOUT with the specified timeout, in
-     *  milliseconds. With this option set to a positive timeout value,
-     *  a call to receive() for this DatagramSocket
-     *  will block for only this amount of time.  If the timeout expires,
-     *  a <B>java.net.SocketTimeoutException</B> is raised, though the
-     *  DatagramSocket is still valid. A timeout of zero is interpreted
-     *  as an infinite timeout.
-     *  The option <B>must</B> be enabled prior to entering the blocking
-     *  operation to have effect.
+    /**
+     * Enable/disable SO_TIMEOUT with the specified timeout, in
+     * milliseconds. With this option set to a positive timeout value,
+     * a call to receive() for this DatagramSocket
+     * will block for only this amount of time.  If the timeout expires,
+     * a <B>java.net.SocketTimeoutException</B> is raised, though the
+     * DatagramSocket is still valid. A timeout of zero is interpreted
+     * as an infinite timeout.
+     * The option <B>must</B> be enabled prior to entering the blocking
+     * operation to have effect.
      *
      * @param timeout the specified timeout in milliseconds.
      * @throws SocketException if there is an error in the underlying protocol, such as an UDP error.
@@ -963,8 +963,7 @@
      * negative.
      * @see #getSendBufferSize()
      */
-    public synchronized void setSendBufferSize(int size)
-    throws SocketException{
+    public synchronized void setSendBufferSize(int size) throws SocketException {
         if (!(size > 0)) {
             throw new IllegalArgumentException("negative send size");
         }
@@ -1021,8 +1020,7 @@
      * negative.
      * @see #getReceiveBufferSize()
      */
-    public synchronized void setReceiveBufferSize(int size)
-    throws SocketException{
+    public synchronized void setReceiveBufferSize(int size) throws SocketException {
         if (size <= 0) {
             throw new IllegalArgumentException("invalid receive size");
         }
@@ -1039,8 +1037,7 @@
      * @throws    SocketException if there is an error in the underlying protocol, such as an UDP error.
      * @see #setReceiveBufferSize(int)
      */
-    public synchronized int getReceiveBufferSize()
-    throws SocketException{
+    public synchronized int getReceiveBufferSize() throws SocketException {
         if (isClosed())
             throw new SocketException("Socket is closed");
         int result = 0;
--- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Fri Nov 01 16:21:17 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Sat Nov 02 10:02:18 2019 +0000
@@ -28,6 +28,8 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
 import java.lang.ref.Cleaner.Cleanable;
 import java.net.DatagramSocket;
 import java.net.Inet4Address;
@@ -39,6 +41,7 @@
 import java.net.ProtocolFamily;
 import java.net.SocketAddress;
 import java.net.SocketOption;
+import java.net.SocketTimeoutException;
 import java.net.StandardProtocolFamily;
 import java.net.StandardSocketOptions;
 import java.nio.ByteBuffer;
@@ -47,6 +50,7 @@
 import java.nio.channels.AsynchronousCloseException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.DatagramChannel;
+import java.nio.channels.IllegalBlockingModeException;
 import java.nio.channels.MembershipKey;
 import java.nio.channels.NotYetConnectedException;
 import java.nio.channels.SelectionKey;
@@ -113,8 +117,17 @@
     private InetSocketAddress localAddress;
     private InetSocketAddress remoteAddress;
 
-    // Our socket adaptor, if any
-    private DatagramSocket socket;
+    // Socket adaptor, created lazily
+    private static final VarHandle SOCKET;
+    static {
+        try {
+            MethodHandles.Lookup l = MethodHandles.lookup();
+            SOCKET = l.findVarHandle(DatagramChannelImpl.class, "socket", DatagramSocket.class);
+        } catch (Exception e) {
+            throw new InternalError(e);
+        }
+    }
+    private volatile DatagramSocket socket;
 
     // Multicast support
     private MembershipRegistry registry;
@@ -199,11 +212,14 @@
 
     @Override
     public DatagramSocket socket() {
-        synchronized (stateLock) {
-            if (socket == null)
-                socket = DatagramSocketAdaptor.create(this);
-            return socket;
+        DatagramSocket socket = this.socket;
+        if (socket == null) {
+            socket = DatagramSocketAdaptor.create(this);
+            if (!SOCKET.compareAndSet(this, null, socket)) {
+                socket = this.socket;
+            }
         }
+        return socket;
     }
 
     @Override
@@ -408,62 +424,35 @@
     public SocketAddress receive(ByteBuffer dst) throws IOException {
         if (dst.isReadOnly())
             throw new IllegalArgumentException("Read-only buffer");
-
         readLock.lock();
         try {
             boolean blocking = isBlocking();
+            boolean completed = false;
             int n = 0;
-            ByteBuffer bb = null;
             try {
                 SocketAddress remote = beginRead(blocking, false);
                 boolean connected = (remote != null);
                 SecurityManager sm = System.getSecurityManager();
+
                 if (connected || (sm == null)) {
                     // connected or no security manager
-                    n = receive(fd, dst, connected);
+                    n = receive(dst, connected);
                     if (blocking) {
                         while (IOStatus.okayToRetry(n) && isOpen()) {
                             park(Net.POLLIN);
-                            n = receive(fd, dst, connected);
+                            n = receive(dst, connected);
                         }
-                    } else if (n == IOStatus.UNAVAILABLE) {
-                        return null;
                     }
                 } else {
-                    // Cannot receive into user's buffer when running with a
-                    // security manager and not connected
-                    bb = Util.getTemporaryDirectBuffer(dst.remaining());
-                    for (;;) {
-                        n = receive(fd, bb, connected);
-                        if (blocking) {
-                            while (IOStatus.okayToRetry(n) && isOpen()) {
-                                park(Net.POLLIN);
-                                n = receive(fd, bb, connected);
-                            }
-                        } else if (n == IOStatus.UNAVAILABLE) {
-                            return null;
-                        }
-                        InetSocketAddress isa = (InetSocketAddress)sender;
-                        try {
-                            sm.checkAccept(isa.getAddress().getHostAddress(),
-                                           isa.getPort());
-                        } catch (SecurityException se) {
-                            // Ignore packet
-                            bb.clear();
-                            n = 0;
-                            continue;
-                        }
-                        bb.flip();
-                        dst.put(bb);
-                        break;
-                    }
+                    // security manager and unconnected
+                    n = untrustedReceive(dst);
                 }
-                assert sender != null;
+                if (n == IOStatus.UNAVAILABLE)
+                    return null;
+                completed = (n > 0) || (n == 0 && isOpen());
                 return sender;
             } finally {
-                if (bb != null)
-                    Util.releaseTemporaryDirectBuffer(bb);
-                endRead(blocking, n > 0);
+                endRead(blocking, completed);
                 assert IOStatus.check(n);
             }
         } finally {
@@ -471,15 +460,164 @@
         }
     }
 
-    private int receive(FileDescriptor fd, ByteBuffer dst, boolean connected)
+    /**
+     * Receives a datagram into an untrusted buffer. When there is a security
+     * manager set, and the socket is not connected, datagrams have to be received
+     * into a buffer that is not accessible to the user. The datagram is copied
+     * into the user's buffer when the sender address is accepted by the security
+     * manager.
+     *
+     * @return the size of the datagram or IOStatus.UNAVAILABLE
+     */
+    private int untrustedReceive(ByteBuffer dst) throws IOException {
+        SecurityManager sm = System.getSecurityManager();
+        assert readLock.isHeldByCurrentThread()
+                && sm != null && remoteAddress == null;
+
+        ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());
+        try {
+            boolean blocking = isBlocking();
+            for (;;) {
+                int n = receive(bb, false);
+                if (blocking) {
+                    while (IOStatus.okayToRetry(n) && isOpen()) {
+                        park(Net.POLLIN);
+                        n = receive(bb, false);
+                    }
+                } else if (n == IOStatus.UNAVAILABLE) {
+                    return n;
+                }
+                InetSocketAddress isa = (InetSocketAddress) sender;
+                try {
+                    sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
+                    bb.flip();
+                    dst.put(bb);
+                    return n;
+                } catch (SecurityException se) {
+                    // ignore datagram
+                    bb.clear();
+                }
+            }
+        } finally {
+            Util.releaseTemporaryDirectBuffer(bb);
+        }
+    }
+
+    /**
+     * Receives a datagram into the given buffer.
+     *
+     * @apiNote This method is for use by the socket adaptor. The buffer is
+     * assumed to be trusted, meaning it is not accessible to user code.
+     *
+     * @throws IllegalBlockingModeException if the channel is non-blocking
+     * @throws SocketTimeoutException if the timeout elapses
+     */
+    SocketAddress blockingReceive(ByteBuffer dst, long nanos) throws IOException {
+        readLock.lock();
+        try {
+            ensureOpen();
+            if (!isBlocking())
+                throw new IllegalBlockingModeException();
+            SecurityManager sm = System.getSecurityManager();
+            boolean connected = isConnected();
+            SocketAddress sender;
+            do {
+                if (nanos > 0) {
+                    sender = trustedBlockingReceive(dst, nanos);
+                } else {
+                    sender = trustedBlockingReceive(dst);
+                }
+                // check sender when security manager set and not connected
+                if (sm != null && !connected) {
+                    InetSocketAddress isa = (InetSocketAddress) sender;
+                    try {
+                        sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
+                    } catch (SecurityException e) {
+                        sender = null;
+                    }
+                }
+            } while (sender == null);
+            return sender;
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    /**
+     * Receives a datagram into given buffer. This method is used to support
+     * the socket adaptor. The buffer is assumed to be trusted.
+     * @throws SocketTimeoutException if the timeout elapses
+     */
+    private SocketAddress trustedBlockingReceive(ByteBuffer dst)
         throws IOException
     {
+        assert readLock.isHeldByCurrentThread() && isBlocking();
+        boolean completed = false;
+        int n = 0;
+        try {
+            SocketAddress remote = beginRead(true, false);
+            boolean connected = (remote != null);
+            n = receive(dst, connected);
+            while (n == IOStatus.UNAVAILABLE && isOpen()) {
+                park(Net.POLLIN);
+                n = receive(dst, connected);
+            }
+            completed = (n > 0) || (n == 0 && isOpen());
+            return sender;
+        } finally {
+            endRead(true, completed);
+            assert IOStatus.check(n);
+        }
+    }
+
+    /**
+     * Receives a datagram into given buffer with a timeout. This method is
+     * used to support the socket adaptor. The buffer is assumed to be trusted.
+     * @throws SocketTimeoutException if the timeout elapses
+     */
+    private SocketAddress trustedBlockingReceive(ByteBuffer dst, long nanos)
+        throws IOException
+    {
+        assert readLock.isHeldByCurrentThread() && isBlocking();
+        boolean completed = false;
+        int n = 0;
+        try {
+            SocketAddress remote = beginRead(true, false);
+            boolean connected = (remote != null);
+
+            // change socket to non-blocking
+            lockedConfigureBlocking(false);
+            try {
+                long startNanos = System.nanoTime();
+                n = receive(dst, connected);
+                while (n == IOStatus.UNAVAILABLE && isOpen()) {
+                    long remainingNanos = nanos - (System.nanoTime() - startNanos);
+                    if (remainingNanos <= 0) {
+                        throw new SocketTimeoutException("Receive timed out");
+                    }
+                    park(Net.POLLIN, remainingNanos);
+                    n = receive(dst, connected);
+                }
+                completed = (n > 0) || (n == 0 && isOpen());
+                return sender;
+            } finally {
+                // restore socket to blocking mode (if channel is open)
+                tryLockedConfigureBlocking(true);
+            }
+
+        } finally {
+            endRead(true, completed);
+            assert IOStatus.check(n);
+        }
+    }
+
+    private int receive(ByteBuffer dst, boolean connected) throws IOException {
         int pos = dst.position();
         int lim = dst.limit();
         assert (pos <= lim);
         int rem = (pos <= lim ? lim - pos : 0);
         if (dst instanceof DirectBuffer && rem > 0)
-            return receiveIntoNativeBuffer(fd, dst, rem, pos, connected);
+            return receiveIntoNativeBuffer(dst, rem, pos, connected);
 
         // Substitute a native buffer. If the supplied buffer is empty
         // we must instead use a nonempty buffer, otherwise the call
@@ -487,7 +625,7 @@
         int newSize = Math.max(rem, 1);
         ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
         try {
-            int n = receiveIntoNativeBuffer(fd, bb, newSize, 0, connected);
+            int n = receiveIntoNativeBuffer(bb, newSize, 0, connected);
             bb.flip();
             if (n > 0 && rem > 0)
                 dst.put(bb);
@@ -497,8 +635,8 @@
         }
     }
 
-    private int receiveIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb,
-                                        int rem, int pos, boolean connected)
+    private int receiveIntoNativeBuffer(ByteBuffer bb, int rem, int pos,
+                                        boolean connected)
         throws IOException
     {
         int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem, connected);
@@ -563,6 +701,25 @@
         }
     }
 
+    /**
+     * Sends a datagram from the bytes in given buffer.
+     *
+     * @apiNote This method is for use by the socket adaptor.
+     *
+     * @throws IllegalBlockingModeException if the channel is non-blocking
+     */
+    void blockingSend(ByteBuffer src, SocketAddress target) throws IOException {
+        writeLock.lock();
+        try {
+            ensureOpen();
+            if (!isBlocking())
+                throw new IllegalBlockingModeException();
+            send(src, target);
+        } finally {
+            writeLock.unlock();
+        }
+    }
+
     private int send(FileDescriptor fd, ByteBuffer src, InetSocketAddress target)
         throws IOException
     {
@@ -785,10 +942,7 @@
         try {
             writeLock.lock();
             try {
-                synchronized (stateLock) {
-                    ensureOpen();
-                    IOUtil.configureBlocking(fd, block);
-                }
+                lockedConfigureBlocking(block);
             } finally {
                 writeLock.unlock();
             }
@@ -797,6 +951,36 @@
         }
     }
 
+    /**
+     * Adjusts the blocking mode. readLock or writeLock must already be held.
+     */
+    private void lockedConfigureBlocking(boolean block) throws IOException {
+        assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
+        synchronized (stateLock) {
+            ensureOpen();
+            IOUtil.configureBlocking(fd, block);
+        }
+    }
+
+    /**
+     * Adjusts the blocking mode if the channel is open. readLock or writeLock
+     * must already be held.
+     *
+     * @return {@code true} if the blocking mode was adjusted, {@code false} if
+     *         the blocking mode was not adjusted because the channel is closed
+     */
+    private boolean tryLockedConfigureBlocking(boolean block) throws IOException {
+        assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
+        synchronized (stateLock) {
+            if (isOpen()) {
+                IOUtil.configureBlocking(fd, block);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
     InetSocketAddress localAddress() {
         synchronized (stateLock) {
             return localAddress;
@@ -861,6 +1045,16 @@
 
     @Override
     public DatagramChannel connect(SocketAddress sa) throws IOException {
+        return connect(sa, true);
+    }
+
+    /**
+     * Connects the channel's socket.
+     *
+     * @param sa the remote address to which this channel is to be connected
+     * @param check true to check if the channel is already connected.
+     */
+    DatagramChannel connect(SocketAddress sa, boolean check) throws IOException {
         InetSocketAddress isa = Net.checkAddress(sa, family);
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
@@ -879,7 +1073,7 @@
             try {
                 synchronized (stateLock) {
                     ensureOpen();
-                    if (state == ST_CONNECTED)
+                    if (check && state == ST_CONNECTED)
                         throw new AlreadyConnectedException();
 
                     // ensure that the socket is bound
@@ -908,7 +1102,7 @@
                     }
                     try {
                         ByteBuffer buf = ByteBuffer.allocate(100);
-                        while (receive(fd, buf, false) > 0) {
+                        while (receive(buf, false) >= 0) {
                             buf.clear();
                         }
                     } finally {
@@ -1332,30 +1526,6 @@
     }
 
     /**
-     * Poll this channel's socket for reading up to the given timeout.
-     * @return {@code true} if the socket is polled
-     */
-    boolean pollRead(long timeout) throws IOException {
-        boolean blocking = isBlocking();
-        assert Thread.holdsLock(blockingLock()) && blocking;
-
-        readLock.lock();
-        try {
-            boolean polled = false;
-            try {
-                beginRead(blocking, false);
-                int events = Net.poll(fd, Net.POLLIN, timeout);
-                polled = (events != 0);
-            } finally {
-                endRead(blocking, polled);
-            }
-            return polled;
-        } finally {
-            readLock.unlock();
-        }
-    }
-
-    /**
      * Translates an interest operation set into a native poll event set
      */
     public int translateInterestOps(int ops) {
--- a/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Fri Nov 01 16:21:17 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Sat Nov 02 10:02:18 2019 +0000
@@ -26,6 +26,9 @@
 package sun.nio.ch;
 
 import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.VarHandle;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.DatagramSocketImpl;
@@ -35,15 +38,16 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.net.SocketOption;
-import java.net.SocketTimeoutException;
 import java.net.StandardSocketOptions;
 import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyConnectedException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.DatagramChannel;
-import java.nio.channels.IllegalBlockingModeException;
-import java.util.Objects;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Set;
 
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
 // Make a datagram-socket channel look like a datagram socket.
 //
@@ -61,13 +65,9 @@
     // Timeout "option" value for receives
     private volatile int timeout;
 
-    // ## super will create a useless impl
+    // create DatagramSocket with useless impl
     private DatagramSocketAdaptor(DatagramChannelImpl dc) {
-        // Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
-        // passing a dummy DatagramSocketImpl object to avoid any native
-        // resource allocation in super class and invoking our bind method
-        // before the dc field is initialized.
-        super(dummyDatagramSocket);
+        super(new DummyDatagramSocketImpl());
         this.dc = dc;
     }
 
@@ -75,17 +75,9 @@
         return new DatagramSocketAdaptor(dc);
     }
 
-    private void connectInternal(SocketAddress remote)
-        throws SocketException
-    {
-        InetSocketAddress isa = Net.asInetSocketAddress(remote);
-        int port = isa.getPort();
-        if (port < 0 || port > 0xFFFF)
-            throw new IllegalArgumentException("connect: " + port);
-        if (remote == null)
-            throw new IllegalArgumentException("connect: null address");
+    private void connectInternal(SocketAddress remote) throws SocketException {
         try {
-            dc.connect(remote);
+            dc.connect(remote, false); // skips check for already connected
         } catch (ClosedChannelException e) {
             // ignore
         } catch (Exception x) {
@@ -95,9 +87,12 @@
 
     @Override
     public void bind(SocketAddress local) throws SocketException {
+        if (local != null) {
+            local = Net.asInetSocketAddress(local);
+        } else {
+            local = new InetSocketAddress(0);
+        }
         try {
-            if (local == null)
-                local = new InetSocketAddress(0);
             dc.bind(local);
         } catch (Exception x) {
             Net.translateToSocketException(x);
@@ -106,17 +101,20 @@
 
     @Override
     public void connect(InetAddress address, int port) {
+        if (address == null)
+            throw new IllegalArgumentException("Address can't be null");
         try {
             connectInternal(new InetSocketAddress(address, port));
         } catch (SocketException x) {
-            // Yes, j.n.DatagramSocket really does this
+            throw new Error(x);
         }
     }
 
     @Override
     public void connect(SocketAddress remote) throws SocketException {
-        Objects.requireNonNull(remote, "Address can't be null");
-        connectInternal(remote);
+        if (remote == null)
+            throw new IllegalArgumentException("Address can't be null");
+        connectInternal(Net.asInetSocketAddress(remote));
     }
 
     @Override
@@ -157,80 +155,84 @@
 
     @Override
     public SocketAddress getLocalSocketAddress() {
-        return dc.localAddress();
+        try {
+            return dc.getLocalAddress();
+        } catch (ClosedChannelException e) {
+            return null;
+        } catch (Exception x) {
+            throw new Error(x);
+        }
     }
 
     @Override
     public void send(DatagramPacket p) throws IOException {
-        synchronized (dc.blockingLock()) {
-            if (!dc.isBlocking())
-                throw new IllegalBlockingModeException();
-            try {
-                synchronized (p) {
-                    ByteBuffer bb = ByteBuffer.wrap(p.getData(),
-                                                    p.getOffset(),
-                                                    p.getLength());
-                    if (dc.isConnected()) {
-                        if (p.getAddress() == null) {
-                            // Legacy DatagramSocket will send in this case
-                            // and set address and port of the packet
-                            InetSocketAddress isa = dc.remoteAddress();
-                            p.setPort(isa.getPort());
-                            p.setAddress(isa.getAddress());
-                            dc.write(bb);
-                        } else {
-                            // Target address may not match connected address
-                            dc.send(bb, p.getSocketAddress());
-                        }
-                    } else {
-                        // Not connected so address must be valid or throw
-                        dc.send(bb, p.getSocketAddress());
+        ByteBuffer bb = null;
+        try {
+            InetSocketAddress target;
+            synchronized (p) {
+                // copy bytes to temporary direct buffer
+                int len = p.getLength();
+                bb = Util.getTemporaryDirectBuffer(len);
+                bb.put(p.getData(), p.getOffset(), len);
+                bb.flip();
+
+                // target address
+                if (p.getAddress() == null) {
+                    InetSocketAddress remote = dc.remoteAddress();
+                    if (remote == null) {
+                        // not specified by DatagramSocket
+                        throw new IllegalArgumentException("Address not set");
                     }
+                    // set address/port to maintain compatibility with DatagramSocket
+                    p.setAddress(remote.getAddress());
+                    p.setPort(remote.getPort());
+                    target = remote;
+                } else {
+                    // throws IllegalArgumentException if port not set
+                    target = (InetSocketAddress) p.getSocketAddress();
                 }
-            } catch (IOException x) {
-                Net.translateException(x);
             }
-        }
-    }
-
-    private SocketAddress receive(ByteBuffer bb) throws IOException {
-        assert Thread.holdsLock(dc.blockingLock()) && dc.isBlocking();
-
-        long to = this.timeout;
-        if (to == 0) {
-            return dc.receive(bb);
-        } else {
-            for (;;) {
-                if (!dc.isOpen())
-                    throw new ClosedChannelException();
-                long st = System.currentTimeMillis();
-                if (dc.pollRead(to)) {
-                    return dc.receive(bb);
-                }
-                to -= System.currentTimeMillis() - st;
-                if (to <= 0)
-                    throw new SocketTimeoutException();
+            // send datagram
+            try {
+                dc.blockingSend(bb, target);
+            } catch (AlreadyConnectedException e) {
+                throw new IllegalArgumentException("Connected and packet address differ");
+            } catch (ClosedChannelException e) {
+                var exc = new SocketException("Socket closed");
+                exc.initCause(e);
+                throw exc;
+            }
+        } finally {
+            if (bb != null) {
+                Util.offerFirstTemporaryDirectBuffer(bb);
             }
         }
     }
 
     @Override
     public void receive(DatagramPacket p) throws IOException {
-        synchronized (dc.blockingLock()) {
-            if (!dc.isBlocking())
-                throw new IllegalBlockingModeException();
-            try {
-                synchronized (p) {
-                    ByteBuffer bb = ByteBuffer.wrap(p.getData(),
-                                                    p.getOffset(),
-                                                    p.getLength());
-                    SocketAddress sender = receive(bb);
-                    p.setSocketAddress(sender);
-                    p.setLength(bb.position() - p.getOffset());
-                }
-            } catch (IOException x) {
-                Net.translateException(x);
+        // get temporary direct buffer with a capacity of p.bufLength
+        int bufLength = DatagramPackets.getBufLength(p);
+        ByteBuffer bb = Util.getTemporaryDirectBuffer(bufLength);
+        try {
+            long nanos = MILLISECONDS.toNanos(timeout);
+            SocketAddress sender = dc.blockingReceive(bb, nanos);
+            bb.flip();
+            synchronized (p) {
+                // copy bytes to the DatagramPacket and set length
+                int len = Math.min(bb.limit(), DatagramPackets.getBufLength(p));
+                bb.get(p.getData(), p.getOffset(), len);
+                DatagramPackets.setLength(p, len);
+
+                // sender address
+                p.setSocketAddress(sender);
             }
+        } catch (ClosedChannelException e) {
+            var exc = new SocketException("Socket closed");
+            exc.initCause(e);
+            throw exc;
+        } finally {
+            Util.offerFirstTemporaryDirectBuffer(bb);
         }
     }
 
@@ -257,19 +259,16 @@
     public int getLocalPort() {
         if (isClosed())
             return -1;
-        try {
-            InetSocketAddress local = dc.localAddress();
-            if (local != null) {
-                return local.getPort();
-            }
-        } catch (Exception x) {
+        InetSocketAddress local = dc.localAddress();
+        if (local != null) {
+            return local.getPort();
         }
         return 0;
     }
 
     @Override
     public void setSoTimeout(int timeout) throws SocketException {
-        if (!dc.isOpen())
+        if (isClosed())
             throw new SocketException("Socket is closed");
         if (timeout < 0)
             throw new IllegalArgumentException("timeout < 0");
@@ -278,7 +277,7 @@
 
     @Override
     public int getSoTimeout() throws SocketException {
-        if (!dc.isOpen())
+        if (isClosed())
             throw new SocketException("Socket is closed");
         return timeout;
     }
@@ -353,7 +352,6 @@
     @Override
     public boolean getReuseAddress() throws SocketException {
         return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
-
     }
 
     @Override
@@ -411,50 +409,157 @@
         return dc.supportedOptions();
     }
 
-   /*
-    * A dummy implementation of DatagramSocketImpl that can be passed to the
-    * DatagramSocket constructor so that no native resources are allocated in
-    * super class.
-    */
-   private static final DatagramSocketImpl dummyDatagramSocket
-       = new DatagramSocketImpl()
-   {
-       protected void create() throws SocketException {}
+
+    /**
+     * DatagramSocketImpl implementation where all methods throw an error.
+     */
+    private static class DummyDatagramSocketImpl extends DatagramSocketImpl {
+        private static <T> T shouldNotGetHere() {
+            throw new InternalError("Should not get here");
+        }
+
+        @Override
+        protected void create() {
+            shouldNotGetHere();
+        }
+
+        @Override
+        protected void bind(int lport, InetAddress laddr) {
+            shouldNotGetHere();
+        }
+
+        @Override
+        protected void send(DatagramPacket p) {
+            shouldNotGetHere();
+        }
+
+        @Override
+        protected int peek(InetAddress address) {
+            return shouldNotGetHere();
+        }
+
+        @Override
+        protected int peekData(DatagramPacket p) {
+            return shouldNotGetHere();
+        }
 
-       protected void bind(int lport, InetAddress laddr) throws SocketException {}
+        @Override
+        protected void receive(DatagramPacket p) {
+            shouldNotGetHere();
+        }
+
+        @Deprecated
+        protected void setTTL(byte ttl) {
+            shouldNotGetHere();
+        }
 
-       protected void send(DatagramPacket p) throws IOException {}
+        @Deprecated
+        protected byte getTTL() {
+            return shouldNotGetHere();
+        }
 
-       protected int peek(InetAddress i) throws IOException { return 0; }
+        @Override
+        protected void setTimeToLive(int ttl) {
+            shouldNotGetHere();
+        }
 
-       protected int peekData(DatagramPacket p) throws IOException { return 0; }
+        @Override
+        protected int getTimeToLive() {
+            return shouldNotGetHere();
+        }
+
+        @Override
+        protected void join(InetAddress group) {
+            shouldNotGetHere();
+        }
 
-       protected void receive(DatagramPacket p) throws IOException {}
+        @Override
+        protected void leave(InetAddress inetaddr) {
+            shouldNotGetHere();
+        }
 
-       @Deprecated
-       protected void setTTL(byte ttl) throws IOException {}
+        @Override
+        protected void joinGroup(SocketAddress group, NetworkInterface netIf) {
+            shouldNotGetHere();
+        }
 
-       @Deprecated
-       protected byte getTTL() throws IOException { return 0; }
+        @Override
+        protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) {
+            shouldNotGetHere();
+        }
 
-       protected void setTimeToLive(int ttl) throws IOException {}
+        @Override
+        protected void close() {
+            shouldNotGetHere();
+        }
+
+        @Override
+        public Object getOption(int optID) {
+            return shouldNotGetHere();
+        }
 
-       protected int getTimeToLive() throws IOException { return 0;}
+        @Override
+        public void setOption(int optID, Object value) {
+            shouldNotGetHere();
+        }
+
+        @Override
+        protected <T> void setOption(SocketOption<T> name, T value) {
+            shouldNotGetHere();
+        }
 
-       protected void join(InetAddress inetaddr) throws IOException {}
+        @Override
+        protected <T> T getOption(SocketOption<T> name) {
+            return shouldNotGetHere();
+        }
 
-       protected void leave(InetAddress inetaddr) throws IOException {}
+        @Override
+        protected Set<SocketOption<?>> supportedOptions() {
+            return shouldNotGetHere();
+        }
+    }
 
-       protected void joinGroup(SocketAddress mcastaddr,
-                                 NetworkInterface netIf) throws IOException {}
-
-       protected void leaveGroup(SocketAddress mcastaddr,
-                                 NetworkInterface netIf) throws IOException {}
+    /**
+     * Defines static methods to get/set DatagramPacket fields and workaround
+     * DatagramPacket deficiencies.
+     */
+    private static class DatagramPackets {
+        private static final VarHandle LENGTH;
+        private static final VarHandle BUF_LENGTH;
+        static {
+            try {
+                PrivilegedAction<Lookup> pa = () -> {
+                    try {
+                        return MethodHandles.privateLookupIn(DatagramPacket.class, MethodHandles.lookup());
+                    } catch (Exception e) {
+                        throw new ExceptionInInitializerError(e);
+                    }
+                };
+                MethodHandles.Lookup l = AccessController.doPrivileged(pa);
+                LENGTH = l.findVarHandle(DatagramPacket.class, "length", int.class);
+                BUF_LENGTH = l.findVarHandle(DatagramPacket.class, "bufLength", int.class);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
 
-       protected void close() {}
-
-       public Object getOption(int optID) throws SocketException { return null;}
+        /**
+         * Sets the DatagramPacket.length field. DatagramPacket.setLength cannot be
+         * used at this time because it sets both the length and bufLength fields.
+         */
+        static void setLength(DatagramPacket p, int value) {
+            synchronized (p) {
+                LENGTH.set(p, value);
+            }
+        }
 
-       public void setOption(int optID, Object value) throws SocketException {}
-   };
-}
+        /**
+         * Returns the value of the DatagramPacket.bufLength field.
+         */
+        static int getBufLength(DatagramPacket p) {
+            synchronized (p) {
+                return (int) BUF_LENGTH.get(p);
+            }
+        }
+    }
+}
\ No newline at end of file
--- a/test/jdk/java/nio/channels/DatagramChannel/AdaptDatagramSocket.java	Fri Nov 01 16:21:17 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-/*
- * Copyright (c) 2001, 2018, 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 4313882 4981129 8143610
- * @summary Unit test for datagram-socket-channel adaptors
- * @library .. /test/lib
- * @build jdk.test.lib.Utils TestServers
- * @run main AdaptDatagramSocket
- * @key randomness
- */
-
-import java.net.*;
-import java.nio.channels.*;
-import java.util.*;
-
-
-public class AdaptDatagramSocket {
-
-    static java.io.PrintStream out = System.out;
-    static Random rand = new Random();
-
-    static String toString(DatagramPacket dp) {
-        return ("DatagramPacket[off=" + dp.getOffset()
-                + ", len=" + dp.getLength()
-                + "]");
-    }
-
-    static void test(DatagramSocket ds, InetSocketAddress dst,
-                     boolean shouldTimeout)
-        throws Exception
-    {
-        DatagramPacket op = new DatagramPacket(new byte[100], 13, 42, dst);
-        rand.nextBytes(op.getData());
-        DatagramPacket ip = new DatagramPacket(new byte[100], 19, 100 - 19);
-        out.println("pre  op: " + toString(op) + "  ip: " + toString(ip));
-
-        long start = System.currentTimeMillis();
-        ds.send(op);
-
-        for (;;) {
-            try {
-                ds.receive(ip);
-                if (ip.getLength() == 0) { // ## Not sure why this happens
-                    ip.setLength(100 - 19);
-                    continue;
-                }
-            } catch (SocketTimeoutException x) {
-                if (shouldTimeout) {
-                    out.println("Receive timed out, as expected");
-                    return;
-                }
-                throw x;
-            }
-            break;
-        }
-
-        out.println("rtt: " + (System.currentTimeMillis() - start));
-        out.println("post op: " + toString(op) + "  ip: " + toString(ip));
-
-        for (int i = 0; i < ip.getLength(); i++) {
-            if (ip.getData()[ip.getOffset() + i]
-                != op.getData()[op.getOffset() + i])
-                throw new Exception("Incorrect data received");
-        }
-
-        if (!(ip.getSocketAddress().equals(dst))) {
-            throw new Exception("Incorrect sender address, expected: " + dst
-                + " actual: " + ip.getSocketAddress());
-        }
-    }
-
-    static void test(InetSocketAddress dst,
-                     int timeout, boolean shouldTimeout,
-                     boolean connect)
-        throws Exception
-    {
-        out.println();
-        out.println("dst: " + dst);
-
-        DatagramSocket ds;
-        if (false) {
-            // Original
-            ds = new DatagramSocket();
-        } else {
-            DatagramChannel dc = DatagramChannel.open();
-            ds = dc.socket();
-            ds.bind(new InetSocketAddress(0));
-        }
-
-        out.println("socket: " + ds);
-        if (connect) {
-            ds.connect(dst);
-            out.println("connect: " + ds);
-        }
-        InetSocketAddress src = new InetSocketAddress(ds.getLocalAddress(),
-                                                      ds.getLocalPort());
-        out.println("src: " + src);
-
-        if (timeout > 0)
-            ds.setSoTimeout(timeout);
-        out.println("timeout: " + ds.getSoTimeout());
-
-        for (int i = 0; i < 5; i++) {
-            test(ds, dst, shouldTimeout);
-        }
-
-        // Leave the socket open so that we don't reuse the old src address
-        //ds.close();
-
-    }
-
-    public static void main(String[] args) throws Exception {
-        // need an UDP echo server
-        try (TestServers.UdpEchoServer echoServer
-                = TestServers.UdpEchoServer.startNewServer(100)) {
-            final InetSocketAddress address
-                = new InetSocketAddress(echoServer.getAddress(),
-                                        echoServer.getPort());
-            test(address, 0, false, false);
-            test(address, 0, false, true);
-            test(address, Integer.MAX_VALUE, false, false);
-        }
-        try (TestServers.UdpDiscardServer discardServer
-                = TestServers.UdpDiscardServer.startNewServer()) {
-            final InetSocketAddress address
-                = new InetSocketAddress(discardServer.getAddress(),
-                                        discardServer.getPort());
-            test(address, 10, true, false);
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorBasic.java	Sat Nov 02 10:02:18 2019 +0000
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2001, 2019, 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 4313882 4981129 8143610 8232673
+ * @summary Unit test for datagram-socket-channel adaptors
+ * @modules java.base/java.net:+open
+ * @library .. /test/lib
+ * @build jdk.test.lib.Utils TestServers
+ * @run main AdaptorBasic
+ * @key randomness
+ */
+
+import java.net.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.lang.reflect.Field;
+
+
+public class AdaptorBasic {
+
+    static java.io.PrintStream out = System.out;
+    static Random rand = new Random();
+
+    static String toString(DatagramPacket dp) {
+        return ("DatagramPacket[off=" + dp.getOffset()
+                + ", len=" + dp.getLength()
+                + "]");
+    }
+
+    static int getBufLength(DatagramPacket p) throws Exception {
+        Field f = DatagramPacket.class.getDeclaredField("bufLength");
+        f.setAccessible(true);
+        return (int) f.get(p);
+    }
+
+    static void test(DatagramSocket ds, InetSocketAddress dst, boolean shouldTimeout)
+        throws Exception
+    {
+        DatagramPacket op = new DatagramPacket(new byte[100], 13, 42, dst);
+        rand.nextBytes(op.getData());
+        int bufLength = 100 - 19;
+        DatagramPacket ip = new DatagramPacket(new byte[100], 19, bufLength);
+        out.println("pre  op: " + toString(op) + "  ip: " + toString(ip));
+
+        long start = System.currentTimeMillis();
+        ds.send(op);
+
+        for (;;) {
+            try {
+                ds.receive(ip);
+            } catch (SocketTimeoutException x) {
+                if (shouldTimeout) {
+                    out.println("Receive timed out, as expected");
+                    return;
+                }
+                throw x;
+            }
+            break;
+        }
+
+        out.println("rtt: " + (System.currentTimeMillis() - start));
+        out.println("post op: " + toString(op) + "  ip: " + toString(ip));
+
+        for (int i = 0; i < ip.getLength(); i++) {
+            if (ip.getData()[ip.getOffset() + i]
+                != op.getData()[op.getOffset() + i])
+                throw new Exception("Incorrect data received");
+        }
+
+        if (!(ip.getSocketAddress().equals(dst))) {
+            throw new Exception("Incorrect sender address, expected: " + dst
+                + " actual: " + ip.getSocketAddress());
+        }
+
+        if (getBufLength(ip) != bufLength) {
+            throw new Exception("DatagramPacket bufLength changed by receive!!!");
+        }
+    }
+
+    static void test(InetSocketAddress dst,
+                     int timeout, boolean shouldTimeout,
+                     boolean connect)
+        throws Exception
+    {
+        out.println();
+        out.println("dst: " + dst);
+
+        DatagramSocket ds;
+        if (false) {
+            // Original
+            ds = new DatagramSocket();
+        } else {
+            DatagramChannel dc = DatagramChannel.open();
+            ds = dc.socket();
+            ds.bind(new InetSocketAddress(0));
+        }
+
+        out.println("socket: " + ds);
+        if (connect) {
+            ds.connect(dst);
+            out.println("connect: " + ds);
+        }
+        InetSocketAddress src = new InetSocketAddress(ds.getLocalAddress(),
+                                                      ds.getLocalPort());
+        out.println("src: " + src);
+
+        if (timeout > 0)
+            ds.setSoTimeout(timeout);
+        out.println("timeout: " + ds.getSoTimeout());
+
+        for (int i = 0; i < 5; i++) {
+            test(ds, dst, shouldTimeout);
+        }
+
+        // Leave the socket open so that we don't reuse the old src address
+        //ds.close();
+
+    }
+
+    public static void main(String[] args) throws Exception {
+        // need an UDP echo server
+        try (TestServers.UdpEchoServer echoServer
+                = TestServers.UdpEchoServer.startNewServer(100)) {
+            final InetSocketAddress address
+                = new InetSocketAddress(echoServer.getAddress(),
+                                        echoServer.getPort());
+            test(address, 0, false, false);
+            test(address, 0, false, true);
+            test(address, Integer.MAX_VALUE, false, false);
+        }
+        try (TestServers.UdpDiscardServer discardServer
+                = TestServers.UdpDiscardServer.startNewServer()) {
+            final InetSocketAddress address
+                = new InetSocketAddress(discardServer.getAddress(),
+                                        discardServer.getPort());
+            test(address, 10, true, false);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConcurrentIO.java	Sat Nov 02 10:02:18 2019 +0000
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019, 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 8232673
+ * @summary Test DatagramChannel socket adaptor with concurrent send/receive
+ */
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.channels.DatagramChannel;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class AdaptorConcurrentIO {
+
+    public static void main(String[] args) throws Exception {
+        testConcurrentSendReceive(0);
+        testConcurrentSendReceive(60_000);
+    }
+
+    /**
+     * Starts a task that blocks in the adaptor's receive method, then invokes
+     * the adaptor's send method to send a datagram. If the adaptor were using
+     * the channel's blockingLock then send without be blocked waiting for
+     * the receive to complete.
+     */
+    static void testConcurrentSendReceive(int timeout) throws Exception {
+        try (DatagramChannel dc = DatagramChannel.open()) {
+            InetAddress lb = InetAddress.getLoopbackAddress();
+            dc.bind(new InetSocketAddress(lb, 0));
+            DatagramSocket s = dc.socket();
+            s.setSoTimeout(timeout);
+
+            ExecutorService pool = Executors.newSingleThreadExecutor();
+            try {
+                Future<String> result = pool.submit(() -> {
+                    byte[] data = new byte[100];
+                    DatagramPacket p = new DatagramPacket(data, 0, data.length);
+                    s.receive(p);
+                    return new String(p.getData(), p.getOffset(), p.getLength(), "UTF-8");
+                });
+
+                Thread.sleep(200); // give chance for thread to block
+
+                byte[] data = "hello".getBytes("UTF-8");
+                DatagramPacket p = new DatagramPacket(data, 0, data.length);
+                p.setSocketAddress(s.getLocalSocketAddress());
+                s.send(p);
+
+                String msg = result.get();
+                if (!msg.equals("hello"))
+                    throw new RuntimeException("Unexpected message: " + msg);
+            } finally {
+                pool.shutdown();
+            }
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java	Sat Nov 02 10:02:18 2019 +0000
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2019, 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 8232673
+ * @summary Test DatagramChannel socket adaptor connect method with illegal args
+ * @run testng AdaptorConnect
+ */
+
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.DatagramChannel;
+import static java.net.InetAddress.getLoopbackAddress;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class AdaptorConnect {
+
+    /**
+     * Invoke the given socket's connect method with illegal arguments.
+     */
+    private void testConnectWithIllegalArguments(DatagramSocket s) {
+        assertThrows(IllegalArgumentException.class, () -> s.connect(null));
+        assertThrows(IllegalArgumentException.class, () -> s.connect(null, 7000));
+        assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), -1));
+        assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), 100_000));
+
+        SocketAddress sillyAddress = new SocketAddress() { };
+        assertThrows(IllegalArgumentException.class, () -> s.connect(sillyAddress));
+
+        SocketAddress unresolved = InetSocketAddress.createUnresolved("foo", 7777);
+        assertThrows(SocketException.class, () -> s.connect(unresolved));
+    }
+
+    /**
+     * Test connect method with an open socket.
+     */
+    public void testOpenSocket() throws Exception {
+        try (DatagramChannel dc = DatagramChannel.open()) {
+            DatagramSocket s = dc.socket();
+
+            testConnectWithIllegalArguments(s);
+
+            // should not be bound or connected
+            assertTrue(s.getLocalSocketAddress() == null);
+            assertTrue(s.getRemoteSocketAddress() == null);
+
+            // connect(SocketAddress)
+            var remote1 = new InetSocketAddress(getLoopbackAddress(), 7001);
+            s.connect(remote1);
+            assertEquals(s.getRemoteSocketAddress(), remote1);
+            testConnectWithIllegalArguments(s);
+            assertEquals(s.getRemoteSocketAddress(), remote1);
+
+            // connect(SocketAddress)
+            var remote2 = new InetSocketAddress(getLoopbackAddress(), 7002);
+            s.connect(remote2);
+            assertEquals(s.getRemoteSocketAddress(), remote2);
+            testConnectWithIllegalArguments(s);
+            assertEquals(s.getRemoteSocketAddress(), remote2);
+
+            // connect(InetAddress, int)
+            var remote3 = new InetSocketAddress(getLoopbackAddress(), 7003);
+            s.connect(remote3.getAddress(), remote3.getPort());
+            assertEquals(s.getRemoteSocketAddress(), remote3);
+            testConnectWithIllegalArguments(s);
+            assertEquals(s.getRemoteSocketAddress(), remote3);
+
+            // connect(InetAddress, int)
+            var remote4 = new InetSocketAddress(getLoopbackAddress(), 7004);
+            s.connect(remote4.getAddress(), remote4.getPort());
+            assertEquals(s.getRemoteSocketAddress(), remote4);
+            testConnectWithIllegalArguments(s);
+            assertEquals(s.getRemoteSocketAddress(), remote4);
+        }
+    }
+
+    /**
+     * Test connect method with a closed socket.
+     */
+    public void testClosedSocket() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        dc.close();
+
+        testConnectWithIllegalArguments(s);
+
+        // connect does not throw an exception when closed
+        var remote = new InetSocketAddress(getLoopbackAddress(), 7001);
+        s.connect(remote);
+        s.connect(remote.getAddress(), remote.getPort());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java	Sat Nov 02 10:02:18 2019 +0000
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2019, 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 8232673
+ * @summary Test the DatagramChannel socket adaptor getter methods
+ * @run testng AdaptorGetters
+ */
+
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.channels.DatagramChannel;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class AdaptorGetters {
+
+    /**
+     * Test getters on unbound socket, before and after it is closed.
+     */
+    public void testUnboundSocket() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        try {
+
+            // state
+            assertFalse(s.isBound());
+            assertFalse(s.isConnected());
+            assertFalse(s.isClosed());
+
+            // local address
+            assertTrue(s.getLocalAddress().isAnyLocalAddress());
+            assertTrue(s.getLocalPort() == 0);
+            assertTrue(s.getLocalSocketAddress() == null);
+
+            // remote address
+            assertTrue(s.getInetAddress() == null);
+            assertTrue(s.getPort() == -1);
+
+        } finally {
+            dc.close();
+        }
+
+        // state
+        assertFalse(s.isBound());
+        assertFalse(s.isConnected());
+        assertTrue(s.isClosed());
+
+        // local address
+        assertTrue(s.getLocalAddress() == null);
+        assertTrue(s.getLocalPort() == -1);
+        assertTrue(s.getLocalSocketAddress() == null);
+
+        // remote address
+        assertTrue(s.getInetAddress() == null);
+        assertTrue(s.getPort() == -1);
+        assertTrue((s.getRemoteSocketAddress() == null));
+    }
+
+    /**
+     * Test getters on bound socket, before and after it is closed.
+     */
+    public void testBoundSocket() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        try {
+            dc.bind(new InetSocketAddress(0));
+            var localAddress = (InetSocketAddress) dc.getLocalAddress();
+
+            // state
+            assertTrue(s.isBound());
+            assertFalse(s.isConnected());
+            assertFalse(s.isClosed());
+
+            // local address
+            assertEquals(s.getLocalAddress(), localAddress.getAddress());
+            assertTrue(s.getLocalPort() == localAddress.getPort());
+            assertEquals(s.getLocalSocketAddress(), localAddress);
+
+            // remote address
+            assertTrue(s.getInetAddress() == null);
+            assertTrue(s.getPort() == -1);
+            assertTrue((s.getRemoteSocketAddress() == null));
+
+        } finally {
+            dc.close();
+        }
+
+        // state
+        assertTrue(s.isBound());
+        assertFalse(s.isConnected());
+        assertTrue(s.isClosed());
+
+        // local address
+        assertTrue(s.getLocalAddress() == null);
+        assertTrue(s.getLocalPort() == -1);
+        assertTrue(s.getLocalSocketAddress() == null);
+
+        // remote address
+        assertTrue(s.getInetAddress() == null);
+        assertTrue(s.getPort() == -1);
+        assertTrue((s.getRemoteSocketAddress() == null));
+    }
+
+    /**
+     * Test getters on connected socket, before and after it is closed.
+     */
+    public void testConnectedSocket() throws Exception {
+        var loopback = InetAddress.getLoopbackAddress();
+        var remoteAddress = new InetSocketAddress(loopback, 7777);
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        try {
+            dc.connect(remoteAddress);
+            var localAddress = (InetSocketAddress) dc.getLocalAddress();
+
+            // state
+            assertTrue(s.isBound());
+            assertTrue(s.isConnected());
+            assertFalse(s.isClosed());
+
+            // local address
+            assertEquals(s.getLocalAddress(), localAddress.getAddress());
+            assertTrue(s.getLocalPort() == localAddress.getPort());
+            assertEquals(s.getLocalSocketAddress(), localAddress);
+
+            // remote address
+            assertEquals(s.getInetAddress(), remoteAddress.getAddress());
+            assertTrue(s.getPort() == remoteAddress.getPort());
+            assertEquals(s.getRemoteSocketAddress(), remoteAddress);
+
+        } finally {
+            dc.close();
+        }
+
+        // state
+        assertTrue(s.isBound());
+        assertTrue(s.isConnected());
+        assertTrue(s.isClosed());
+
+        // local address
+        assertTrue(s.getLocalAddress() == null);
+        assertTrue(s.getLocalPort() == -1);
+        assertTrue(s.getLocalSocketAddress() == null);
+
+        // remote address
+        assertEquals(s.getInetAddress(), remoteAddress.getAddress());
+        assertTrue(s.getPort() == remoteAddress.getPort());
+        assertEquals(s.getRemoteSocketAddress(), remoteAddress);
+    }
+
+    /**
+     * Test getters on disconnected socket, before and after it is closed.
+     */
+    public void testDisconnectedSocket() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        try {
+            var loopback = InetAddress.getLoopbackAddress();
+            dc.connect(new InetSocketAddress(loopback, 7777));
+            dc.disconnect();
+
+            var localAddress = (InetSocketAddress) dc.getLocalAddress();
+
+            // state
+            assertTrue(s.isBound());
+            assertFalse(s.isConnected());
+            assertFalse(s.isClosed());
+
+            // local address
+            assertEquals(s.getLocalAddress(), localAddress.getAddress());
+            assertTrue(s.getLocalPort() == localAddress.getPort());
+            assertEquals(s.getLocalSocketAddress(), localAddress);
+
+            // remote address
+            assertTrue(s.getInetAddress() == null);
+            assertTrue(s.getPort() == -1);
+            assertTrue((s.getRemoteSocketAddress() == null));
+
+
+        } finally {
+            dc.close();
+        }
+
+        // state
+        assertTrue(s.isBound());
+        assertFalse(s.isConnected());
+        assertTrue(s.isClosed());
+
+        // local address
+        assertTrue(s.getLocalAddress() == null);
+        assertTrue(s.getLocalPort() == -1);
+        assertTrue(s.getLocalSocketAddress() == null);
+
+        // remote address
+        assertTrue(s.getInetAddress() == null);
+        assertTrue(s.getPort() == -1);
+        assertTrue((s.getRemoteSocketAddress() == null));
+    }
+}
--- a/test/jdk/java/nio/channels/etc/AdaptorCloseAndInterrupt.java	Fri Nov 01 16:21:17 2019 -0400
+++ b/test/jdk/java/nio/channels/etc/AdaptorCloseAndInterrupt.java	Sat Nov 02 10:02:18 2019 +0000
@@ -22,7 +22,7 @@
  */
 
 /* @test
- * @bug 7184932
+ * @bug 7184932 8232673
  * @summary Test asynchronous close and interrupt of timed socket adapter methods
  * @key randomness intermittent
  */
@@ -78,8 +78,10 @@
 
             try (DatagramChannel peer = DatagramChannel.open()) {
                 peer.socket().bind(null);
-                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose();
-                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt();
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(0);
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(30_000);
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(0);
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(30_000);
             }
 
             new AdaptorCloseAndInterrupt().ssAcceptAsyncClose();
@@ -138,11 +140,10 @@
         }
     }
 
-    void dcReceiveAsyncClose() throws IOException {
+    void dcReceiveAsyncClose(int timeout) throws IOException {
         DatagramChannel dc = DatagramChannel.open();
-        dc.connect(new InetSocketAddress(
-            InetAddress.getLoopbackAddress(), port));
-        dc.socket().setSoTimeout(30*1000);
+        dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
+        dc.socket().setSoTimeout(timeout);
 
         doAsyncClose(dc);
 
@@ -150,24 +151,23 @@
             dc.socket().receive(new DatagramPacket(new byte[100], 100));
             System.err.format("close() was invoked: %s%n", isClosed.get());
             throw new RuntimeException("receive should not have completed");
-        } catch (ClosedChannelException expected) {}
+        } catch (SocketException expected) { }
 
         if (!dc.socket().isClosed())
             throw new RuntimeException("socket is not closed");
     }
 
-    void dcReceiveAsyncInterrupt() throws IOException {
+    void dcReceiveAsyncInterrupt(int timeout) throws IOException {
         DatagramChannel dc = DatagramChannel.open();
-        dc.connect(new InetSocketAddress(
-            InetAddress.getLoopbackAddress(), port));
-        dc.socket().setSoTimeout(30*1000);
+        dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
+        dc.socket().setSoTimeout(timeout);
 
         doAsyncInterrupt();
 
         try {
             dc.socket().receive(new DatagramPacket(new byte[100], 100));
             throw new RuntimeException("receive should not have completed");
-        } catch (ClosedByInterruptException expected) {
+        } catch (SocketException expected) {
             System.out.format("interrupt() was invoked: %s%n",
                 isInterrupted.get());
             System.out.format("dcReceiveAsyncInterrupt was interrupted: %s%n",