Merge niosocketimpl-branch
authoralanb
Sat, 09 Mar 2019 12:54:20 +0000
branchniosocketimpl-branch
changeset 57250 b38f280d2114
parent 57249 3e39753ed05b (diff)
parent 54043 0324b3756aa2 (current diff)
child 57252 d70fc9bc1430
Merge
make/devkit/createMacosxDevkit6.sh
make/devkit/createMacosxDevkit9.sh
make/devkit/createWindowsDevkit2013.sh
make/devkit/createWindowsDevkit2015.sh
src/hotspot/os_cpu/linux_aarch64/linux_aarch64.ad
src/hotspot/os_cpu/linux_sparc/linux_sparc.ad
src/hotspot/share/classfile/dictionary.inline.hpp
src/hotspot/share/prims/evmCompat.cpp
src/hotspot/share/utilities/intHisto.cpp
src/hotspot/share/utilities/intHisto.hpp
src/java.base/share/classes/com/sun/net/ssl/HostnameVerifier.java
src/java.base/share/classes/com/sun/net/ssl/HttpsURLConnection.java
src/java.base/share/classes/com/sun/net/ssl/KeyManager.java
src/java.base/share/classes/com/sun/net/ssl/KeyManagerFactory.java
src/java.base/share/classes/com/sun/net/ssl/KeyManagerFactorySpi.java
src/java.base/share/classes/com/sun/net/ssl/SSLContext.java
src/java.base/share/classes/com/sun/net/ssl/SSLContextSpi.java
src/java.base/share/classes/com/sun/net/ssl/SSLPermission.java
src/java.base/share/classes/com/sun/net/ssl/SSLSecurity.java
src/java.base/share/classes/com/sun/net/ssl/TrustManager.java
src/java.base/share/classes/com/sun/net/ssl/TrustManagerFactory.java
src/java.base/share/classes/com/sun/net/ssl/TrustManagerFactorySpi.java
src/java.base/share/classes/com/sun/net/ssl/X509KeyManager.java
src/java.base/share/classes/com/sun/net/ssl/X509TrustManager.java
src/java.base/share/classes/com/sun/net/ssl/internal/ssl/Provider.java
src/java.base/share/classes/com/sun/net/ssl/internal/ssl/X509ExtendedTrustManager.java
src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java
src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/Handler.java
src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/HttpsURLConnectionOldImpl.java
src/java.base/share/classes/com/sun/net/ssl/package-info.java
src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/BaseStructure.java
src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Marshaller.java
src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/XmlWriter.java
src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/XmlWriterToTree.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java
test/hotspot/gtest/memory/test_virtualSpaceNode.cpp
test/jdk/com/sun/net/ssl/SSLSecurity/ComKeyManagerFactoryImpl.java
test/jdk/com/sun/net/ssl/SSLSecurity/ComSSLContextImpl.java
test/jdk/com/sun/net/ssl/SSLSecurity/ComTrustManagerFactoryImpl.java
test/jdk/com/sun/net/ssl/SSLSecurity/JavaxKeyManagerFactoryImpl.java
test/jdk/com/sun/net/ssl/SSLSecurity/JavaxSSLContextImpl.java
test/jdk/com/sun/net/ssl/SSLSecurity/JavaxTrustManagerFactoryImpl.java
test/jdk/com/sun/net/ssl/SSLSecurity/ProviderTest.java
test/jdk/com/sun/net/ssl/SSLSecurity/TruncateArray.java
test/jdk/javax/net/ssl/FixingJavadocs/ComURLNulls.java
test/jdk/javax/net/ssl/SSLSession/CheckMyTrustedKeystore.java
test/jdk/sun/net/www/protocol/https/NewImpl/ComHTTPSConnection.java
test/jdk/sun/net/www/protocol/https/NewImpl/ComHostnameVerifier.java
test/jdk/sun/security/ssl/X509TrustManagerImpl/ClientServer.java
--- a/src/java.base/share/classes/java/lang/System.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/java/lang/System.java	Sat Mar 09 12:54:20 2019 +0000
@@ -2172,6 +2172,12 @@
             public void blockedOn(Interruptible b) {
                 Thread.blockedOn(b);
             }
+            public void setNativeTid(long tid) {
+                Thread.setNativeTid(tid);
+            }
+            public long nativeTid() {
+                return Thread.nativeTid();
+            }
             public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
                 Shutdown.add(slot, registerShutdownInProgress, hook);
             }
--- a/src/java.base/share/classes/java/lang/Thread.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/java/lang/Thread.java	Sat Mar 09 12:54:20 2019 +0000
@@ -241,6 +241,18 @@
     }
 
     /**
+     * Native thread id, cached here for use for threads are blocked in I/O
+     * operations.
+     */
+    private long nativeTid;
+    static void setNativeTid(long tid) {
+        Thread.currentThread().nativeTid = tid;
+    }
+    static long nativeTid() {
+        return Thread.currentThread().nativeTid;
+    }
+
+    /**
      * The minimum priority that a thread can have.
      */
     public static final int MIN_PRIORITY = 1;
--- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java	Sat Mar 09 12:54:20 2019 +0000
@@ -30,12 +30,16 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
 import sun.net.ConnectionResetException;
 import sun.net.NetHooks;
+import sun.net.PlatformSocketImpl;
 import sun.net.ResourceManager;
 import sun.net.util.SocketExceptions;
 
@@ -46,7 +50,7 @@
  *
  * @author  Steven B. Byrne
  */
-abstract class AbstractPlainSocketImpl extends SocketImpl {
+abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSocketImpl {
     /* instance variable for SO_TIMEOUT */
     int timeout;   // timeout in millisec
     // traffic class
@@ -450,15 +454,17 @@
 
     /**
      * Accepts connections.
-     * @param s the connection
+     * @param si the socket impl
      */
-    protected void accept(SocketImpl s) throws IOException {
+    protected void accept(SocketImpl si) throws IOException {
+        si.fd = new FileDescriptor();
         acquireFD();
         try {
-            socketAccept(s);
+            socketAccept(si);
         } finally {
             releaseFD();
         }
+        SocketCleanable.register(si.fd);
     }
 
     /**
@@ -470,8 +476,14 @@
                 throw new IOException("Socket Closed");
             if (shut_rd)
                 throw new IOException("Socket input is shutdown");
-            if (socketInputStream == null)
-                socketInputStream = new SocketInputStream(this);
+            if (socketInputStream == null) {
+                PrivilegedExceptionAction<SocketInputStream> pa = () -> new SocketInputStream(this);
+                try {
+                    socketInputStream = AccessController.doPrivileged(pa);
+                } catch (PrivilegedActionException e) {
+                    throw (IOException) e.getCause();
+                }
+            }
         }
         return socketInputStream;
     }
@@ -489,8 +501,14 @@
                 throw new IOException("Socket Closed");
             if (shut_wr)
                 throw new IOException("Socket output is shutdown");
-            if (socketOutputStream == null)
-                socketOutputStream = new SocketOutputStream(this);
+            if (socketOutputStream == null) {
+                PrivilegedExceptionAction<SocketOutputStream> pa = () -> new SocketOutputStream(this);
+                try {
+                    socketOutputStream = AccessController.doPrivileged(pa);
+                } catch (PrivilegedActionException e) {
+                    throw (IOException) e.getCause();
+                }
+            }
         }
         return socketOutputStream;
     }
@@ -589,15 +607,10 @@
         }
     }
 
-    void reset() throws IOException {
-        if (fd != null) {
-            socketClose();
-        }
-        fd = null;
-        super.reset();
+    void reset() {
+        throw new InternalError("should not get here");
     }
 
-
     /**
      * Shutdown read-half of the socket connection;
      */
@@ -714,6 +727,42 @@
         socketClose0(false);
     }
 
+    @Override
+    public void copyTo(SocketImpl si) {
+        // this SocketImpl should be connected
+        assert fd.valid() && localport != 0 && address != null && port != 0;
+
+        if (si instanceof AbstractPlainSocketImpl) {
+            AbstractPlainSocketImpl psi = (AbstractPlainSocketImpl) si;
+            try {
+                psi.close();
+            } catch (IOException ignore) { }
+
+            // copy fields
+            psi.stream = this.stream;
+            psi.fd = this.fd;
+            psi.localport = this.localport;
+            psi.address = this.address;
+            psi.port = this.port;
+
+            // reset fields; do not reset timeout
+            psi.closePending = false;
+            psi.connectionReset = false;
+            psi.shut_rd = false;
+            psi.shut_wr = false;
+        } else {
+            // copy fields
+            si.fd = this.fd;
+            si.localport = this.localport;
+            si.address = this.address;
+            si.port = this.port;
+        }
+
+        // this SocketImpl is now closed and should be discarded
+        this.closePending = true;
+        this.fd = null;
+    }
+
     abstract void socketCreate(boolean isServer) throws IOException;
     abstract void socketConnect(InetAddress address, int port, int timeout)
         throws IOException;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/net/DelegatingSocketImpl.java	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,171 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package java.net;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Objects;
+import java.util.Set;
+
+import sun.net.PlatformSocketImpl;
+
+/**
+ * A SocketImpl that delegates all methods to another SocketImpl.
+ */
+
+class DelegatingSocketImpl extends SocketImpl {
+    protected final SocketImpl delegate;
+
+    DelegatingSocketImpl(SocketImpl delegate) {
+        assert delegate instanceof PlatformSocketImpl;
+        this.delegate = Objects.requireNonNull(delegate);
+    }
+
+    final SocketImpl delegate() {
+        return delegate;
+    }
+
+    @Override
+    protected FileDescriptor getFileDescriptor() {
+        return delegate.getFileDescriptor();
+    }
+
+    @Override
+    protected InetAddress getInetAddress() {
+        return delegate.getInetAddress();
+    }
+
+    @Override
+    protected int getPort() {
+        return delegate.getPort();
+    }
+
+    @Override
+    protected int getLocalPort() {
+        return delegate.getLocalPort();
+    }
+
+    @Override
+    protected void create(boolean stream) throws IOException {
+        delegate.create(stream);
+    }
+
+    @Override
+    protected void connect(String host, int port) throws IOException {
+        delegate.connect(host, port);
+    }
+
+    @Override
+    protected void connect(InetAddress address, int port) throws IOException {
+        delegate.connect(address, port);
+    }
+
+    @Override
+    protected void connect(SocketAddress address, int timeout) throws IOException {
+        delegate.connect(address, timeout);
+    }
+
+    @Override
+    protected void bind(InetAddress host, int port) throws IOException {
+        delegate.bind(host, port);
+    }
+
+    @Override
+    protected void listen(int backlog) throws IOException {
+        delegate.listen(backlog);
+    }
+
+    @Override
+    protected void accept(SocketImpl s) throws IOException {
+        delegate.accept(s);
+    }
+
+    @Override
+    protected InputStream getInputStream() throws IOException {
+        return delegate.getInputStream();
+    }
+
+    @Override
+    protected OutputStream getOutputStream() throws IOException {
+        return delegate.getOutputStream();
+    }
+
+    @Override
+    protected int available() throws IOException {
+        return delegate.available();
+    }
+
+    @Override
+    protected void close() throws IOException {
+        delegate.close();
+    }
+
+    @Override
+    protected boolean supportsUrgentData() {
+        return delegate.supportsUrgentData();
+    }
+
+    @Override
+    protected void sendUrgentData(int data) throws IOException {
+        delegate.sendUrgentData(data);
+    }
+
+    @Override
+    protected Set<SocketOption<?>> supportedOptions() {
+        return delegate.supportedOptions();
+    }
+
+    @Override
+    protected <T> void setOption(SocketOption<T> opt, T value) throws IOException {
+        delegate.setOption(opt, value);
+    }
+
+    @Override
+    protected <T> T getOption(SocketOption<T> opt) throws IOException {
+        return delegate.getOption(opt);
+    }
+
+    @Override
+    public void setOption(int optID, Object value) throws SocketException {
+        delegate.setOption(optID, value);
+    }
+
+    @Override
+    public Object getOption(int optID) throws SocketException {
+        return delegate.getOption(optID);
+    }
+
+    @Override
+    protected void shutdownInput() throws IOException {
+        delegate.shutdownInput();
+    }
+
+    @Override
+    protected void shutdownOutput() throws IOException {
+        delegate.shutdownOutput();
+    }
+}
--- a/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java	Sat Mar 09 12:54:20 2019 +0000
@@ -34,14 +34,13 @@
 
 /**
  * Basic SocketImpl that relies on the internal HTTP protocol handler
- * implementation to perform the HTTP tunneling and authentication. The
- * sockets impl is swapped out and replaced with the socket from the HTTP
- * handler after the tunnel is successfully setup.
+ * implementation to perform the HTTP tunneling and authentication. Once
+ * connected, all socket operations delegate to a platform SocketImpl.
  *
  * @since 1.8
  */
 
-/*package*/ class HttpConnectSocketImpl extends PlainSocketImpl {
+/*package*/ class HttpConnectSocketImpl extends DelegatingSocketImpl {
 
     private static final String httpURLClazzStr =
                                   "sun.net.www.protocol.http.HttpURLConnection";
@@ -76,12 +75,8 @@
         }
     }
 
-    HttpConnectSocketImpl(String server, int port) {
-        this.server = server;
-        this.port = port;
-    }
-
-    HttpConnectSocketImpl(Proxy proxy) {
+    HttpConnectSocketImpl(Proxy proxy, SocketImpl delegate) {
+        super(delegate);
         SocketAddress a = proxy.address();
         if ( !(a instanceof InetSocketAddress) )
             throw new IllegalArgumentException("Unsupported address type");
@@ -92,6 +87,27 @@
     }
 
     @Override
+    protected void connect(String host, int port) throws IOException {
+        connect(new InetSocketAddress(host, port), 0);
+    }
+
+    @Override
+    protected void connect(InetAddress address, int port) throws IOException {
+        connect(new InetSocketAddress(address, port), 0);
+    }
+
+    @Override
+    void setSocket(Socket socket) {
+        delegate.socket = socket;
+        super.setSocket(socket);
+    }
+
+    @Override
+    void setServerSocket(ServerSocket socket) {
+        throw new InternalError("should not get here");
+    }
+
+    @Override
     protected void connect(SocketAddress endpoint, int timeout)
         throws IOException
     {
@@ -117,21 +133,37 @@
         close();
 
         // update the Sockets impl to the impl from the http Socket
-        AbstractPlainSocketImpl psi = (AbstractPlainSocketImpl) httpSocket.impl;
-        this.getSocket().impl = psi;
+        SocketImpl si = httpSocket.impl;
+        getSocket().setImpl(si);
 
         // best effort is made to try and reset options previously set
         Set<Map.Entry<Integer,Object>> options = optionsMap.entrySet();
         try {
             for(Map.Entry<Integer,Object> entry : options) {
-                psi.setOption(entry.getKey(), entry.getValue());
+                si.setOption(entry.getKey(), entry.getValue());
             }
         } catch (IOException x) {  /* gulp! */  }
     }
 
+
+    @Override
+    protected void listen(int backlog) {
+        throw new InternalError("should not get here");
+    }
+
+    @Override
+    protected void accept(SocketImpl s) {
+        throw new InternalError("should not get here");
+    }
+
+    @Override
+    void reset() {
+        throw new InternalError("should not get here");
+    }
+
     @Override
     public void setOption(int opt, Object val) throws SocketException {
-        super.setOption(opt, val);
+        delegate.setOption(opt, val);
 
         if (external_address != null)
             return;  // we're connected, just return
@@ -163,7 +195,10 @@
         URL destURL = new URL(urlString);
         HttpURLConnection conn = (HttpURLConnection) destURL.openConnection(proxy);
         conn.setConnectTimeout(connectTimeout);
-        conn.setReadTimeout(this.timeout);
+        int timeout = (int) getOption(SocketOptions.SO_TIMEOUT);
+        if (timeout > 0) {
+            conn.setReadTimeout(timeout);
+        }
         conn.connect();
         doTunneling(conn);
         try {
@@ -174,10 +209,14 @@
         }
     }
 
-    private void doTunneling(HttpURLConnection conn) {
+    private void doTunneling(HttpURLConnection conn) throws IOException {
         try {
             doTunneling.invoke(conn);
         } catch (ReflectiveOperationException x) {
+            Throwable cause = x.getCause();
+            if (cause instanceof IOException) {
+                throw (IOException) cause;
+            }
             throw new InternalError("Should not reach here", x);
         }
     }
@@ -187,7 +226,7 @@
         if (external_address != null)
             return external_address.getAddress();
         else
-            return super.getInetAddress();
+            return delegate.getInetAddress();
     }
 
     @Override
@@ -195,6 +234,6 @@
         if (external_address != null)
             return external_address.getPort();
         else
-            return super.getPort();
+            return delegate.getPort();
     }
 }
--- a/src/java.base/share/classes/java/net/ServerSocket.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/java/net/ServerSocket.java	Sat Mar 09 12:54:20 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 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
@@ -25,9 +25,6 @@
 
 package java.net;
 
-import jdk.internal.access.JavaNetSocketAccess;
-import jdk.internal.access.SharedSecrets;
-
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
@@ -38,6 +35,10 @@
 import java.util.Set;
 import java.util.Collections;
 
+import jdk.internal.access.JavaNetSocketAccess;
+import jdk.internal.access.SharedSecrets;
+import sun.net.PlatformSocketImpl;
+
 /**
  * This class implements server sockets. A server socket waits for
  * requests to come in over the network. It performs some operation
@@ -290,13 +291,12 @@
     }
 
     private void setImpl() {
+        SocketImplFactory factory = ServerSocket.factory;
         if (factory != null) {
             impl = factory.createSocketImpl();
             checkOldImpl();
         } else {
-            // No need to do a checkOldImpl() here, we know it's an up to date
-            // SocketImpl!
-            impl = new SocksSocketImpl();
+            impl = SocketImpl.createPlatformSocketImpl(true);
         }
         if (impl != null)
             impl.setServerSocket(this);
@@ -542,38 +542,99 @@
      * @spec JSR-51
      */
     protected final void implAccept(Socket s) throws IOException {
-        SocketImpl si = null;
-        try {
-            if (s.impl == null)
-              s.setImpl();
-            else {
-                s.impl.reset();
+        SocketImpl si = s.impl;
+
+        // Socket has no SocketImpl
+        if (si == null) {
+            // create a platform or custom SocketImpl and accept the connection
+            SocketImplFactory factory = Socket.socketImplFactory();
+            if (factory == null) {
+                si = SocketImpl.createPlatformSocketImpl(false);
+            } else {
+                si = factory.createSocketImpl();
             }
-            si = s.impl;
-            s.impl = null;
-            si.address = new InetAddress();
-            si.fd = new FileDescriptor();
-            getImpl().accept(si);
-            SocketCleanable.register(si.fd);   // raw fd has been set
+            implAccept(si);
+            // bind Socket to the SocketImpl and update socket state
+            s.setImpl(si);
+            s.postAccept();
+            return;
+        }
+
+        // Socket has a SOCKS or HTTP SocketImpl, need delegate
+        if (si instanceof DelegatingSocketImpl) {
+            si = ((DelegatingSocketImpl) si).delegate();
+            assert si instanceof PlatformSocketImpl;
+        }
+
+        // ServerSocket or Socket (or both) have, or delegate to, a platform SocketImpl
+        if (impl instanceof PlatformSocketImpl || si instanceof PlatformSocketImpl) {
+            // create a new platform SocketImpl and accept the connection
+            var psi = SocketImpl.createPlatformSocketImpl(false);
+            implAccept(psi);
+            // copy connection/state to the existing SocketImpl and update socket state
+            psi.copyTo(si);
+            s.postAccept();
+            return;
+        }
 
-            SecurityManager security = System.getSecurityManager();
-            if (security != null) {
-                security.checkAccept(si.getInetAddress().getHostAddress(),
-                                     si.getPort());
+        // ServerSocket and Socket bound to custom SocketImpls
+        s.impl = null; // break connection to impl
+        si.reset();
+        try {
+            implAccept(si);
+        } catch (Exception e) {
+            si.reset();
+            throw e;
+        } finally {
+            s.impl = si;  // restore connection to impl
+        }
+        s.postAccept();
+    }
+
+    /**
+     * Accepts a new connection so that the given SocketImpl is connected to
+     * the peer. The SocketImpl and connection are closed if the connection is
+     * denied by the security manager.
+     * @throws IOException if an I/O error occurs
+     * @throws SecurityException if the security manager's checkAccept method fails
+     */
+    private void implAccept(SocketImpl si) throws IOException {
+        assert !(si instanceof DelegatingSocketImpl);
+
+        // A non-platform SocketImpl cannot accept a connection with a platform SocketImpl
+        if (!(impl instanceof PlatformSocketImpl) && (si instanceof PlatformSocketImpl)) {
+            throw new IOException("An instance of " + impl.getClass() +
+                " cannot accept a connection with an instance of " + si.getClass());
+        }
+
+        // custom SocketImpl may expect fd/address objects to be created
+        if (!(si instanceof PlatformSocketImpl)) {
+            si.fd = new FileDescriptor();
+            si.address = new InetAddress();
+        }
+
+        // accept a connection
+        impl.accept(si);
+
+        // sanity check that the fields defined by SocketImpl have been set
+        if (si instanceof PlatformSocketImpl) {
+            var fd = si.fd;
+            if (fd == null || !fd.valid() || si.localport <= 0
+                    || si.address == null || si.port <= 0) {
+                throw new IOException("Invalid accepted state:" + si);
             }
-        } catch (IOException e) {
-            if (si != null)
-                si.reset();
-            s.impl = si;
-            throw e;
-        } catch (SecurityException e) {
-            if (si != null)
-                si.reset();
-            s.impl = si;
-            throw e;
         }
-        s.impl = si;
-        s.postAccept();
+
+        // check permission, close SocketImpl/connection if denied
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            try {
+                sm.checkAccept(si.getInetAddress().getHostAddress(), si.getPort());
+            } catch (SecurityException se) {
+                si.close();
+                throw se;
+            }
+        }
     }
 
     /**
@@ -661,6 +722,8 @@
     public synchronized void setSoTimeout(int timeout) throws SocketException {
         if (isClosed())
             throw new SocketException("Socket is closed");
+        if (timeout < 0)
+            throw new IllegalArgumentException("timeout can't be negative");
         getImpl().setOption(SocketOptions.SO_TIMEOUT, timeout);
     }
 
@@ -778,7 +841,7 @@
     /**
      * The factory for all server sockets.
      */
-    private static SocketImplFactory factory = null;
+    private static volatile SocketImplFactory factory;
 
     /**
      * Sets the server socket implementation factory for the
--- a/src/java.base/share/classes/java/net/Socket.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/java/net/Socket.java	Sat Mar 09 12:54:20 2019 +0000
@@ -28,9 +28,10 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
 import java.nio.channels.SocketChannel;
 import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
 import java.security.PrivilegedAction;
 import java.util.Set;
 import java.util.Collections;
@@ -76,6 +77,22 @@
     private boolean oldImpl = false;
 
     /**
+     * Socket input/output streams
+     */
+    private volatile InputStream in;
+    private volatile OutputStream out;
+    private static final VarHandle IN, OUT;
+    static {
+        try {
+            MethodHandles.Lookup l = MethodHandles.lookup();
+            IN = l.findVarHandle(Socket.class, "in", InputStream.class);
+            OUT = l.findVarHandle(Socket.class, "out", OutputStream.class);
+        } catch (Exception e) {
+            throw new InternalError(e);
+        }
+    }
+
+    /**
      * Creates an unconnected socket, with the
      * system-default type of SocketImpl.
      *
@@ -137,16 +154,22 @@
                     security.checkConnect(epoint.getAddress().getHostAddress(),
                                   epoint.getPort());
             }
-            impl = type == Proxy.Type.SOCKS ? new SocksSocketImpl(p)
-                                            : new HttpConnectSocketImpl(p);
+
+            // create a SOCKS or HTTP SocketImpl that delegates to a platform SocketImpl
+            SocketImpl delegate = SocketImpl.createPlatformSocketImpl(false);
+            impl = (type == Proxy.Type.SOCKS) ? new SocksSocketImpl(p, delegate)
+                                              : new HttpConnectSocketImpl(p, delegate);
             impl.setSocket(this);
         } else {
             if (p == Proxy.NO_PROXY) {
+                // create a platform or custom SocketImpl for the DIRECT case
+                SocketImplFactory factory = Socket.factory;
                 if (factory == null) {
-                    impl = new PlainSocketImpl();
-                    impl.setSocket(this);
-                } else
-                    setImpl();
+                    impl = SocketImpl.createPlatformSocketImpl(false);
+                } else {
+                    impl = factory.createSocketImpl();
+                }
+                impl.setSocket(this);
             } else
                 throw new IllegalArgumentException("Invalid Proxy");
         }
@@ -491,24 +514,29 @@
         });
     }
 
+    void setImpl(SocketImpl si) {
+         impl = si;
+         impl.setSocket(this);
+    }
+
     /**
      * Sets impl to the system-default type of SocketImpl.
      * @since 1.4
      */
     void setImpl() {
+        SocketImplFactory factory = Socket.factory;
         if (factory != null) {
             impl = factory.createSocketImpl();
             checkOldImpl();
         } else {
-            // No need to do a checkOldImpl() here, we know it's an up to date
-            // SocketImpl!
-            impl = new SocksSocketImpl();
+            // create a SOCKS SocketImpl that delegates to a platform SocketImpl
+            SocketImpl delegate = SocketImpl.createPlatformSocketImpl(false);
+            impl = new SocksSocketImpl(delegate);
         }
         if (impl != null)
             impl.setSocket(this);
     }
 
-
     /**
      * Get the {@code SocketImpl} attached to this socket, creating
      * it if necessary.
@@ -907,18 +935,49 @@
             throw new SocketException("Socket is not connected");
         if (isInputShutdown())
             throw new SocketException("Socket input is shutdown");
-        InputStream is = null;
-        try {
-            is = AccessController.doPrivileged(
-                new PrivilegedExceptionAction<>() {
-                    public InputStream run() throws IOException {
-                        return impl.getInputStream();
-                    }
-                });
-        } catch (java.security.PrivilegedActionException e) {
-            throw (IOException) e.getException();
+        InputStream in = this.in;
+        if (in == null) {
+            // wrap the input stream so that the close method closes this socket
+            in = new SocketInputStream(this, impl.getInputStream());
+            if (!IN.compareAndSet(this, null, in)) {
+                in = this.in;
+            }
         }
-        return is;
+        return in;
+    }
+
+    /**
+     * An InputStream that delegates read/available operations to an underlying
+     * input stream. The close method is overridden to close the Socket.
+     *
+     * This class is instrumented by Java Flight Recorder (JFR) to get socket
+     * I/O events.
+     */
+    private static class SocketInputStream extends InputStream {
+        private final Socket parent;
+        private final InputStream in;
+        SocketInputStream(Socket parent, InputStream in) {
+            this.parent = parent;
+            this.in = in;
+        }
+        @Override
+        public int read() throws IOException {
+            byte[] a = new byte[1];
+            int n = read(a, 0, 1);
+            return (n > 0) ? (a[0] & 0xff) : -1;
+        }
+        @Override
+        public int read(byte b[], int off, int len) throws IOException {
+            return in.read(b, off, len);
+        }
+        @Override
+        public int available() throws IOException {
+            return in.available();
+        }
+        @Override
+        public void close() throws IOException {
+            parent.close();
+        }
     }
 
     /**
@@ -946,18 +1005,44 @@
             throw new SocketException("Socket is not connected");
         if (isOutputShutdown())
             throw new SocketException("Socket output is shutdown");
-        OutputStream os = null;
-        try {
-            os = AccessController.doPrivileged(
-                new PrivilegedExceptionAction<>() {
-                    public OutputStream run() throws IOException {
-                        return impl.getOutputStream();
-                    }
-                });
-        } catch (java.security.PrivilegedActionException e) {
-            throw (IOException) e.getException();
+        OutputStream out = this.out;
+        if (out == null) {
+            // wrap the output stream so that the close method closes this socket
+            out = new SocketOutputStream(this, impl.getOutputStream());
+            if (!OUT.compareAndSet(this, null, out)) {
+                out = this.out;
+            }
         }
-        return os;
+        return out;
+    }
+
+    /**
+     * An OutputStream that delegates write operations to an underlying output
+     * stream. The close method is overridden to close the Socket.
+     *
+     * This class is instrumented by Java Flight Recorder (JFR) to get socket
+     * I/O events.
+     */
+    private static class SocketOutputStream extends OutputStream {
+        private final Socket parent;
+        private final OutputStream out;
+        SocketOutputStream(Socket parent, OutputStream out) {
+            this.parent = parent;
+            this.out = out;
+        }
+        @Override
+        public void write(int b) throws IOException {
+            byte[] a = new byte[] { (byte) b };
+            write(a, 0, 1);
+        }
+        @Override
+        public void write(byte b[], int off, int len) throws IOException {
+            out.write(b, off, len);
+        }
+        @Override
+        public void close() throws IOException {
+            parent.close();
+        }
     }
 
     /**
@@ -1381,9 +1466,8 @@
         try {
             getImpl().setOption(SocketOptions.IP_TOS, tc);
         } catch (SocketException se) {
-            // not supported if socket already connected
-            // Solaris returns error in such cases
-            if(!isConnected())
+            // may not be supported to change when socket is connected
+            if (!isConnected())
                 throw se;
         }
     }
@@ -1644,7 +1728,11 @@
     /**
      * The factory for all client sockets.
      */
-    private static SocketImplFactory factory = null;
+    private static volatile SocketImplFactory factory;
+
+    static SocketImplFactory socketImplFactory() {
+        return factory;
+    }
 
     /**
      * Sets the client socket implementation factory for the
@@ -1757,7 +1845,14 @@
      * @since 9
      */
     public <T> Socket setOption(SocketOption<T> name, T value) throws IOException {
-        getImpl().setOption(name, value);
+        try {
+            getImpl().setOption(name, value);
+        } catch (SocketException se) {
+            // may not be supported to change when socket is connected
+            if (name != StandardSocketOptions.IP_TOS || !isConnected()) {
+                throw se;
+            }
+        }
         return this;
     }
 
--- a/src/java.base/share/classes/java/net/SocketImpl.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/java/net/SocketImpl.java	Sat Mar 09 12:54:20 2019 +0000
@@ -25,12 +25,18 @@
 
 package java.net;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.FileDescriptor;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Set;
 
+import sun.net.NetProperties;
+import sun.net.PlatformSocketImpl;
+import sun.nio.ch.NioSocketImpl;
+
 /**
  * The abstract class {@code SocketImpl} is a common superclass
  * of all classes that actually implement sockets. It is used to
@@ -43,6 +49,26 @@
  * @since   1.0
  */
 public abstract class SocketImpl implements SocketOptions {
+    private static final boolean USE_PLAINSOCKETIMPL = usePlainSocketImpl();
+
+    private static boolean usePlainSocketImpl() {
+        PrivilegedAction<String> pa = () -> NetProperties.get("jdk.net.usePlainSocketImpl");
+        String s = AccessController.doPrivileged(pa);
+        return (s != null) && !s.equalsIgnoreCase("false");
+    }
+
+    /**
+     * Creates an instance of platform's SocketImpl
+     */
+    @SuppressWarnings("unchecked")
+    static <S extends SocketImpl & PlatformSocketImpl> S createPlatformSocketImpl(boolean server) {
+        if (USE_PLAINSOCKETIMPL) {
+            return (S) new PlainSocketImpl();
+        } else {
+            return (S) new NioSocketImpl(server);
+        }
+    }
+
     /**
      * The actual Socket object.
      */
@@ -306,7 +332,8 @@
             ",port=" + getPort() + ",localport=" + getLocalPort()  + "]";
     }
 
-    void reset() throws IOException {
+    void reset() {
+        fd = null;
         address = null;
         port = 0;
         localport = 0;
--- a/src/java.base/share/classes/java/net/SocksSocketImpl.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/java/net/SocksSocketImpl.java	Sat Mar 09 12:54:20 2019 +0000
@@ -23,6 +23,7 @@
  * questions.
  */
 package java.net;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -36,11 +37,9 @@
 
 /**
  * SOCKS (V4 & V5) TCP socket implementation (RFC 1928).
- * This is a subclass of PlainSocketImpl.
- * Note this class should <b>NOT</b> be public.
  */
 
-class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
+class SocksSocketImpl extends DelegatingSocketImpl implements SocksConsts {
     private String server = null;
     private int serverPort = DEFAULT_PORT;
     private InetSocketAddress external_address;
@@ -49,11 +48,12 @@
     private InputStream cmdIn = null;
     private OutputStream cmdOut = null;
 
-    SocksSocketImpl() {
-        // Nothing needed
+    SocksSocketImpl(SocketImpl delegate) {
+        super(delegate);
     }
 
-    SocksSocketImpl(Proxy proxy) {
+    SocksSocketImpl(Proxy proxy, SocketImpl delegate) {
+        super(delegate);
         SocketAddress a = proxy.address();
         if (a instanceof InetSocketAddress) {
             InetSocketAddress ad = (InetSocketAddress) a;
@@ -75,7 +75,7 @@
     private synchronized void privilegedConnect(final String host,
                                               final int port,
                                               final int timeout)
-         throws IOException
+        throws IOException
     {
         try {
             AccessController.doPrivileged(
@@ -94,7 +94,7 @@
 
     private void superConnectServer(String host, int port,
                                     int timeout) throws IOException {
-        super.connect(new InetSocketAddress(host, port), timeout);
+        delegate.connect(new InetSocketAddress(host, port), timeout);
     }
 
     private static int remainingMillis(long deadlineMillis) throws IOException {
@@ -111,16 +111,23 @@
     private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException {
         int len = data.length;
         int received = 0;
-        while (received < len) {
-            int count;
-            try {
-                count = ((SocketInputStream)in).read(data, received, len - received, remainingMillis(deadlineMillis));
-            } catch (SocketTimeoutException e) {
-                throw new SocketTimeoutException("Connect timed out");
+        int originalTimeout = (int) getOption(SocketOptions.SO_TIMEOUT);
+        try {
+            while (received < len) {
+                int count;
+                int remaining = remainingMillis(deadlineMillis);
+                setOption(SocketOptions.SO_TIMEOUT, remaining);
+                try {
+                    count = in.read(data, received, len - received);
+                } catch (SocketTimeoutException e) {
+                    throw new SocketTimeoutException("Connect timed out");
+                }
+                if (count < 0)
+                    throw new SocketException("Malformed reply from SOCKS server");
+                received += count;
             }
-            if (count < 0)
-                throw new SocketException("Malformed reply from SOCKS server");
-            received += count;
+        } finally {
+            setOption(SocketOptions.SO_TIMEOUT, originalTimeout);
         }
         return received;
     }
@@ -239,6 +246,27 @@
         }
     }
 
+    @Override
+    protected void connect(String host, int port) throws IOException {
+        connect(new InetSocketAddress(host, port), 0);
+    }
+
+    @Override
+    protected void connect(InetAddress address, int port) throws IOException {
+        connect(new InetSocketAddress(address, port), 0);
+    }
+
+    @Override
+    void setSocket(Socket soc) {
+        delegate.socket = soc;
+        super.setSocket(soc);
+    }
+
+    @Override
+    void setServerSocket(ServerSocket soc) {
+        throw new InternalError("should not get here");
+    }
+
     /**
      * Connects the Socks Socket to the specified endpoint. It will first
      * connect to the SOCKS proxy and negotiate the access. If the proxy
@@ -290,7 +318,7 @@
                 /*
                  * No default proxySelector --> direct connection
                  */
-                super.connect(epoint, remainingMillis(deadlineMillis));
+                delegate.connect(epoint, remainingMillis(deadlineMillis));
                 return;
             }
             URI uri;
@@ -313,13 +341,13 @@
             java.util.Iterator<Proxy> iProxy = null;
             iProxy = sel.select(uri).iterator();
             if (iProxy == null || !(iProxy.hasNext())) {
-                super.connect(epoint, remainingMillis(deadlineMillis));
+                delegate.connect(epoint, remainingMillis(deadlineMillis));
                 return;
             }
             while (iProxy.hasNext()) {
                 p = iProxy.next();
                 if (p == null || p.type() != Proxy.Type.SOCKS) {
-                    super.connect(epoint, remainingMillis(deadlineMillis));
+                    delegate.connect(epoint, remainingMillis(deadlineMillis));
                     return;
                 }
 
@@ -509,7 +537,15 @@
         external_address = epoint;
     }
 
+    @Override
+    protected void listen(int backlog) {
+        throw new InternalError("should not get here");
+    }
 
+    @Override
+    protected void accept(SocketImpl s) {
+        throw new InternalError("should not get here");
+    }
 
     /**
      * Returns the value of this socket's {@code address} field.
@@ -522,7 +558,7 @@
         if (external_address != null)
             return external_address.getAddress();
         else
-            return super.getInetAddress();
+            return delegate.getInetAddress();
     }
 
     /**
@@ -536,7 +572,7 @@
         if (external_address != null)
             return external_address.getPort();
         else
-            return super.getPort();
+            return delegate.getPort();
     }
 
     @Override
@@ -544,10 +580,15 @@
         if (cmdsock != null)
             cmdsock.close();
         cmdsock = null;
-        super.close();
+        delegate.close();
     }
 
     private String getUserName() {
         return StaticProperty.userName();
     }
+
+    @Override
+    void reset() {
+        throw new InternalError("should not get here");
+    }
 }
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java	Sat Mar 09 12:54:20 2019 +0000
@@ -107,6 +107,16 @@
     void blockedOn(Interruptible b);
 
     /**
+     * Set the current thread's native ID
+     */
+    void setNativeTid(long tid);
+
+    /**
+     * Returns the current thread's native ID
+     */
+    long nativeTid();
+
+    /**
      * Registers a shutdown hook.
      *
      * It is expected that this method with registerShutdownInProgress=true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/net/PlatformSocketImpl.java	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,43 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package sun.net;
+
+import java.net.SocketImpl;
+
+/**
+ * Implemented by the platform's SocketImpl implementations.
+ */
+
+public interface PlatformSocketImpl {
+
+    /**
+     * Copy the state from this connected SocketImpl to a target SocketImpl. If
+     * the target SocketImpl is not a newly created SocketImpl then it is first
+     * closed to release any resources. The target SocketImpl becomes the owner
+     * of the file descriptor, this SocketImpl is marked as closed and should
+     * be discarded.
+     */
+    void copyTo(SocketImpl si);
+}
--- a/src/java.base/share/classes/sun/nio/ch/IOStatus.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/sun/nio/ch/IOStatus.java	Sat Mar 09 12:54:20 2019 +0000
@@ -81,4 +81,12 @@
         return ((n > EOF) || (n < UNSUPPORTED_CASE));
     }
 
+    /**
+     * Returns true if the error code is UNAVAILABLE or INTERRUPTED, the
+     * error codes to indicate that an I/O operation can be retried.
+     */
+    static boolean okayToRetry(long n) {
+        return (n == IOStatus.UNAVAILABLE) || (n == IOStatus.INTERRUPTED);
+    }
+
 }
--- a/src/java.base/share/classes/sun/nio/ch/Net.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/sun/nio/ch/Net.java	Sat Mar 09 12:54:20 2019 +0000
@@ -310,6 +310,12 @@
     static final ExtendedSocketOptions extendedOptions =
             ExtendedSocketOptions.getInstance();
 
+    static void setSocketOption(FileDescriptor fd, SocketOption<?> name, Object value)
+        throws IOException
+    {
+        setSocketOption(fd, Net.UNSPEC, name, value);
+    }
+
     static void setSocketOption(FileDescriptor fd, ProtocolFamily family,
                                 SocketOption<?> name, Object value)
         throws IOException
@@ -372,8 +378,13 @@
         setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg, isIPv6);
     }
 
-    static Object getSocketOption(FileDescriptor fd, ProtocolFamily family,
-                                  SocketOption<?> name)
+    static Object getSocketOption(FileDescriptor fd, SocketOption<?> name)
+        throws IOException
+    {
+        return getSocketOption(fd, Net.UNSPEC, name);
+    }
+
+    static Object getSocketOption(FileDescriptor fd, ProtocolFamily family, SocketOption<?> name)
         throws IOException
     {
         Class<?> type = name.type();
@@ -426,8 +437,7 @@
         return socket(UNSPEC, stream);
     }
 
-    static FileDescriptor socket(ProtocolFamily family, boolean stream)
-        throws IOException {
+    static FileDescriptor socket(ProtocolFamily family, boolean stream) throws IOException {
         boolean preferIPv6 = isIPv6Available() &&
             (family != StandardProtocolFamily.INET);
         return IOUtil.newFD(socket0(preferIPv6, stream, false, fastLoopback));
@@ -524,6 +534,10 @@
     static native int poll(FileDescriptor fd, int events, long timeout)
         throws IOException;
 
+    static int pollNow(FileDescriptor fd, int events) throws IOException {
+        return poll(fd, events, 0);
+    }
+
     /**
      * Polls a connecting socket to test if the connection has been established.
      *
@@ -535,6 +549,10 @@
     public static native int pollConnect(FileDescriptor fd, long timeout)
         throws IOException;
 
+    static int pollConnectNow(FileDescriptor fd) throws IOException {
+        return pollConnect(fd, 0);
+    }
+
     /**
      * Return the number of bytes in the socket input buffer.
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,1324 @@
+/*
+ * Copyright (c) 2018, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.nio.ch;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ProtocolFamily;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.SocketImpl;
+import java.net.SocketOption;
+import java.net.SocketTimeoutException;
+import java.net.StandardProtocolFamily;
+import java.net.StandardSocketOptions;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+import jdk.internal.access.SharedSecrets;
+import jdk.internal.ref.CleanerFactory;
+import sun.net.ConnectionResetException;
+import sun.net.NetHooks;
+import sun.net.PlatformSocketImpl;
+import sun.net.ResourceManager;
+import sun.net.ext.ExtendedSocketOptions;
+import sun.net.util.SocketExceptions;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+/**
+ * NIO based SocketImpl.
+ *
+ * This implementation attempts to be compatible with legacy PlainSocketImpl,
+ * including behavior and exceptions that are not specified by SocketImpl.
+ *
+ * The underlying socket used by this SocketImpl is initially configured
+ * blocking. If a connect, accept or read is attempted with a timeout then the
+ * socket is changed to non-blocking mode. When in non-blocking mode, operations
+ * that don't complete immediately will poll the socket.
+ */
+
+public final class NioSocketImpl extends SocketImpl implements PlatformSocketImpl {
+    private static final NativeDispatcher nd = new SocketDispatcher(true);
+
+    // The maximum number of bytes to read/write per syscall to avoid needing
+    // a huge buffer from the temporary buffer cache
+    private static final int MAX_BUFFER_SIZE = 128 * 1024;
+
+    // true if this is a SocketImpl for a ServerSocket
+    private final boolean server;
+
+    // Lock held when reading (also used when accepting or connecting)
+    private final ReentrantLock readLock = new ReentrantLock();
+
+    // Lock held when writing
+    private final ReentrantLock writeLock = new ReentrantLock();
+
+    // The stateLock for read/changing state
+    private final Object stateLock = new Object();
+    private static final int ST_NEW = 0;
+    private static final int ST_UNCONNECTED = 1;
+    private static final int ST_CONNECTING = 2;
+    private static final int ST_CONNECTED = 3;
+    private static final int ST_CLOSING = 4;
+    private static final int ST_CLOSED = 5;
+    private volatile int state;  // need stateLock to change
+
+    // set by SocketImpl.create, protected by stateLock
+    private boolean stream;
+    private FileDescriptorCloser closer;
+
+    // lazily set to true when the socket is configured non-blocking
+    private volatile boolean nonBlocking;
+
+    // used by connect/read/write/accept, protected by stateLock
+    private long readerThread;
+    private long writerThread;
+
+    // used when SO_REUSEADDR is emulated, protected by stateLock
+    private boolean isReuseAddress;
+
+    // cached value of IPV6_TCLASS or IP_TOS socket option, protected by stateLock
+    private int trafficClass;
+
+    // read or accept timeout in millis
+    private volatile int timeout;
+
+    // flags to indicate if the connection is shutdown for input and output
+    private volatile boolean isInputClosed;
+    private volatile boolean isOutputClosed;
+
+    // used by read to emulate legacy behavior, protected by readLock
+    private boolean readEOF;
+    private boolean connectionReset;
+
+    /**
+     * Creates an instance of this SocketImpl.
+     * @param server true if this is a SocketImpl for a ServerSocket
+     */
+    public NioSocketImpl(boolean server) {
+        this.server = server;
+    }
+
+    /**
+     * Returns true if the socket is open.
+     */
+    private boolean isOpen() {
+        return state < ST_CLOSING;
+    }
+
+    /**
+     * Throws SocketException if the socket is not open.
+     */
+    private void ensureOpen() throws SocketException {
+        int state = this.state;
+        if (state == ST_NEW)
+            throw new SocketException("Socket not created");
+        if (state >= ST_CLOSING)
+            throw new SocketException("Socket closed");
+    }
+
+    /**
+     * Throws SocketException if the socket is not open and connected.
+     */
+    private void ensureOpenAndConnected() throws SocketException {
+        int state = this.state;
+        if (state < ST_CONNECTED)
+            throw new SocketException("Not connected");
+        if (state > ST_CONNECTED)
+            throw new SocketException("Socket closed");
+    }
+
+    /**
+     * Disables the current thread for scheduling purposes until the socket is
+     * ready for I/O, or is asynchronously closed, for up to the specified
+     * waiting time.
+     * @throws IOException if an I/O error occurs
+     */
+    private void park(FileDescriptor fd, int event, long nanos) throws IOException {
+        long millis;
+        if (nanos == 0) {
+            millis = -1;
+        } else {
+            millis = MILLISECONDS.convert(nanos, NANOSECONDS);
+        }
+        Net.poll(fd, event, millis);
+    }
+
+    /**
+     * Disables the current thread for scheduling purposes until the socket is
+     * ready for I/O or is asynchronously closed.
+     * @throws IOException if an I/O error occurs
+     */
+    private void park(FileDescriptor fd, int event) throws IOException {
+        park(fd, event, 0);
+    }
+
+    /**
+     * Ensures that the socket is configured non-blocking when a timeout is specified.
+     * @throws IOException if there is an I/O error changing the blocking mode
+     */
+    private void configureNonBlockingIfNeeded(FileDescriptor fd, int timeout)
+        throws IOException
+    {
+        if (timeout > 0 && !nonBlocking) {
+            assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
+            IOUtil.configureBlocking(fd, false);
+            nonBlocking = true;
+        }
+    }
+
+    /**
+     * Marks the beginning of a read operation that might block.
+     * @throws SocketException if the socket is closed or not connected
+     */
+    private FileDescriptor beginRead() throws SocketException {
+        synchronized (stateLock) {
+            ensureOpenAndConnected();
+            readerThread = NativeThread.current();
+            return fd;
+        }
+    }
+
+    /**
+     * Marks the end of a read operation that may have blocked.
+     * @throws SocketException is the socket is closed
+     */
+    private void endRead(boolean completed) throws SocketException {
+        synchronized (stateLock) {
+            readerThread = 0;
+            int state = this.state;
+            if (state == ST_CLOSING)
+                stateLock.notifyAll();
+            if (!completed && state >= ST_CLOSING)
+                throw new SocketException("Socket closed");
+        }
+    }
+
+    /**
+     * Attempts to read bytes from the socket into the given byte array.
+     */
+    private int tryRead(FileDescriptor fd, byte[] b, int off, int len)
+        throws IOException
+    {
+        ByteBuffer dst = Util.getTemporaryDirectBuffer(len);
+        assert dst.position() == 0;
+        try {
+            int n = nd.read(fd, ((DirectBuffer)dst).address(), len);
+            if (n > 0) {
+                dst.get(b, off, n);
+            }
+            return n;
+        } finally{
+            Util.offerFirstTemporaryDirectBuffer(dst);
+        }
+    }
+
+    /**
+     * Reads bytes from the socket into the given byte array with a timeout.
+     * @throws SocketTimeoutException if the read timeout elapses
+     */
+    private int timedRead(FileDescriptor fd, byte[] b, int off, int len, int millis)
+        throws IOException
+    {
+        assert nonBlocking;
+        long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
+        long remainingNanos = nanos;
+        long startNanos = System.nanoTime();
+        int n;
+        do {
+            park(fd, Net.POLLIN, remainingNanos);
+            n = tryRead(fd, b, off, len);
+            if (n == IOStatus.UNAVAILABLE) {
+                remainingNanos = nanos - (System.nanoTime() - startNanos);
+                if (remainingNanos <= 0) {
+                    throw new SocketTimeoutException("Read timed out");
+                }
+            }
+        } while (n == IOStatus.UNAVAILABLE && isOpen());
+        return n;
+    }
+
+    /**
+     * Reads bytes from the socket into the given byte array.
+     * @return the number of bytes read or -1 at EOF
+     * @throws SocketException if the socket is closed or a socket I/O error occurs
+     * @throws SocketTimeoutException if the read timeout elapses
+     */
+    private int implRead(byte[] b, int off, int len) throws IOException {
+        readLock.lock();
+        try {
+            // emulate legacy behavior to return -1, even if socket is closed
+            if (readEOF)
+                return -1;
+            int n = 0;
+            FileDescriptor fd = beginRead();
+            try {
+                if (connectionReset)
+                    throw new SocketException("Connection reset");
+                if (isInputClosed)
+                    return -1;
+                int timeout = this.timeout;
+                configureNonBlockingIfNeeded(fd, timeout);
+                n = tryRead(fd, b, off, len);
+                if (IOStatus.okayToRetry(n) && isOpen()) {
+                    if (timeout > 0) {
+                        // read with timeout
+                        n = timedRead(fd, b, off, len, timeout);
+                    } else {
+                        // read, no timeout
+                        do {
+                            park(fd, Net.POLLIN);
+                            n = tryRead(fd, b, off, len);
+                        } while (IOStatus.okayToRetry(n) && isOpen());
+                    }
+                }
+                if (n == -1)
+                    readEOF = true;
+                return n;
+            } catch (SocketTimeoutException e) {
+                throw e;
+            } catch (ConnectionResetException e) {
+                connectionReset = true;
+                throw new SocketException("Connection reset");
+            } catch (IOException ioe) {
+                throw new SocketException(ioe.getMessage());
+            } finally {
+                endRead(n > 0);
+            }
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    /**
+     * Reads bytes from the socket into the given byte array.
+     * @return the number of bytes read or -1 at EOF
+     * @throws IndexOutOfBoundsException if the bound checks fail
+     * @throws SocketException if the socket is closed or a socket I/O error occurs
+     * @throws SocketTimeoutException if the read timeout elapses
+     */
+    private int read(byte[] b, int off, int len) throws IOException {
+        Objects.checkFromIndexSize(off, len, b.length);
+        if (len == 0) {
+            return 0;
+        } else {
+            // read up to MAX_BUFFER_SIZE bytes
+            int size = Math.min(len, MAX_BUFFER_SIZE);
+            return implRead(b, off, size);
+        }
+    }
+
+    /**
+     * Marks the beginning of a write operation that might block.
+     * @throws SocketException if the socket is closed or not connected
+     */
+    private FileDescriptor beginWrite() throws SocketException {
+        synchronized (stateLock) {
+            ensureOpenAndConnected();
+            writerThread = NativeThread.current();
+            return fd;
+        }
+    }
+
+    /**
+     * Marks the end of a write operation that may have blocked.
+     * @throws SocketException is the socket is closed
+     */
+    private void endWrite(boolean completed) throws SocketException {
+        synchronized (stateLock) {
+            writerThread = 0;
+            int state = this.state;
+            if (state == ST_CLOSING)
+                stateLock.notifyAll();
+            if (!completed && state >= ST_CLOSING)
+                throw new SocketException("Socket closed");
+        }
+    }
+
+    /**
+     * Attempts to write a sequence of bytes to the socket from the given
+     * byte array.
+     */
+    private int tryWrite(FileDescriptor fd, byte[] b, int off, int len)
+        throws IOException
+    {
+        ByteBuffer src = Util.getTemporaryDirectBuffer(len);
+        assert src.position() == 0;
+        try {
+            src.put(b, off, len);
+            return nd.write(fd, ((DirectBuffer)src).address(), len);
+        } finally {
+            Util.offerFirstTemporaryDirectBuffer(src);
+        }
+    }
+
+    /**
+     * Writes a sequence of bytes to the socket from the given byte array.
+     * @return the number of bytes written
+     * @throws SocketException if the socket is closed or an socket I/O error occurs
+     */
+    private int implWrite(byte[] b, int off, int len) throws IOException {
+        writeLock.lock();
+        try {
+            int n = 0;
+            FileDescriptor fd = beginWrite();
+            try {
+                n = tryWrite(fd, b, off, len);
+                while (IOStatus.okayToRetry(n) && isOpen()) {
+                    park(fd, Net.POLLOUT);
+                    n = tryWrite(fd, b, off, len);
+                }
+                return n;
+            } catch (IOException ioe) {
+                throw new SocketException(ioe.getMessage());
+            } finally {
+                endWrite(n > 0);
+            }
+        } finally {
+            writeLock.unlock();
+        }
+    }
+
+    /**
+     * Writes a sequence of bytes to the socket from the given byte array.
+     * @throws SocketException if the socket is closed or an socket I/O error occurs
+     */
+    private void write(byte[] b, int off, int len) throws IOException {
+        Objects.checkFromIndexSize(off, len, b.length);
+        if (len > 0) {
+            int pos = off;
+            int end = off + len;
+            while (pos < end) {
+                // write up to MAX_BUFFER_SIZE bytes
+                int size = Math.min((end - pos), MAX_BUFFER_SIZE);
+                int n = implWrite(b, pos, size);
+                pos += n;
+            }
+        }
+    }
+
+    /**
+     * Creates the socket.
+     * @param stream {@code true} for a streams socket
+     */
+    @Override
+    protected void create(boolean stream) throws IOException {
+        synchronized (stateLock) {
+            if (state != ST_NEW)
+                throw new IOException("Already created");
+            if (!stream)
+                ResourceManager.beforeUdpCreate();
+            FileDescriptor fd;
+            try {
+                if (server) {
+                    assert stream;
+                    fd = Net.serverSocket(true);
+                } else {
+                    fd = Net.socket(stream);
+                }
+            } catch (IOException ioe) {
+                if (!stream)
+                    ResourceManager.afterUdpClose();
+                throw ioe;
+            }
+            this.fd = fd;
+            this.stream = stream;
+            this.closer = FileDescriptorCloser.create(this);
+            this.state = ST_UNCONNECTED;
+        }
+    }
+
+    /**
+     * For use by ServerSocket to copy the state from this connected SocketImpl
+     * to a target SocketImpl. If the target SocketImpl is not a newly created
+     * SocketImpl then it is first closed to release any resources. The target
+     * SocketImpl becomes the owner of the file descriptor, this SocketImpl
+     * is marked as closed and should be discarded.
+     */
+    @Override
+    public void copyTo(SocketImpl si) {
+        if (si instanceof NioSocketImpl) {
+            NioSocketImpl nsi = (NioSocketImpl) si;
+            if (nsi.state != ST_NEW) {
+                try {
+                    nsi.close();
+                } catch (IOException ignore) { }
+            }
+            // copy/reset fields protected by stateLock
+            synchronized (nsi.stateLock) {
+                guarantee(nsi.state == ST_NEW || nsi.state == ST_CLOSED);
+                synchronized (this.stateLock) {
+                    // this SocketImpl should be connected
+                    guarantee(state == ST_CONNECTED && fd != null && fd.valid()
+                        && localport > 0 && address != null && port > 0);
+
+                    // copy fields
+                    nsi.stream = this.stream;
+                    nsi.fd = this.fd;
+                    nsi.localport = this.localport;
+                    nsi.address = this.address;
+                    nsi.port = this.port;
+
+                    // reset fields; do not reset timeout
+                    nsi.nonBlocking = false;
+                    nsi.isReuseAddress = false;
+                    nsi.isInputClosed = false;
+                    nsi.isOutputClosed = false;
+                    nsi.state = ST_CONNECTED;
+
+                    // GC'ing of this impl should not close the file descriptor
+                    this.closer.disable();
+                    this.state = ST_CLOSED;
+
+                    // create new closer to execute when nsi is GC'ed
+                    nsi.closer = FileDescriptorCloser.create(nsi);
+                }
+            }
+            // reset fields protected by readLock
+            nsi.readLock.lock();
+            try {
+                nsi.readEOF = false;
+                nsi.connectionReset = false;
+            } finally {
+                nsi.readLock.unlock();
+            }
+        } else {
+            synchronized (this.stateLock) {
+                // this SocketImpl should be connected
+                guarantee(state == ST_CONNECTED && fd != null && fd.valid()
+                        && localport > 0 && address != null && port > 0);
+
+                // set fields in foreign impl
+                setSocketImplFields(si, fd, localport, address, port);
+
+                // disable closer to prevent GC'ing of this impl from
+                // closing the file descriptor
+                this.closer.disable();
+                this.state = ST_CLOSED;
+            }
+        }
+    }
+
+    /**
+     * Marks the beginning of a connect operation that might block.
+     * @throws SocketException if the socket is closed or already connected
+     */
+    private FileDescriptor beginConnect(InetAddress address, int port)
+        throws IOException
+    {
+        synchronized (stateLock) {
+            int state = this.state;
+            if (state != ST_UNCONNECTED) {
+                if (state == ST_CONNECTING)
+                    throw new SocketException("Connection in progress");
+                if (state == ST_CONNECTED)
+                    throw new SocketException("Already connected");
+                if (state >= ST_CLOSING)
+                    throw new SocketException("Socket closed");
+                assert false;
+            }
+            this.state = ST_CONNECTING;
+
+            // invoke beforeTcpConnect hook if not already bound
+            if (localport == 0) {
+                NetHooks.beforeTcpConnect(fd, address, port);
+            }
+
+            // save the remote address/port
+            this.address = address;
+            this.port = port;
+
+            readerThread = NativeThread.current();
+            return fd;
+        }
+    }
+
+    /**
+     * Marks the end of a connect operation that may have blocked.
+     * @throws SocketException is the socket is closed
+     */
+    private void endConnect(boolean completed) throws IOException {
+        synchronized (stateLock) {
+            readerThread = 0;
+            int state = this.state;
+            if (state == ST_CLOSING)
+                stateLock.notifyAll();
+            if (completed && state == ST_CONNECTING) {
+                this.state = ST_CONNECTED;
+                localport = Net.localAddress(fd).getPort();
+            } else if (!completed && state >= ST_CLOSING) {
+                throw new SocketException("Socket closed");
+            }
+        }
+    }
+
+    /**
+     * Waits for a connection attempt to finish with a timeout.
+     * @throws SocketTimeoutException if the connect timeout elapses
+     */
+    private int timedFinishConnect(FileDescriptor fd, int millis) throws IOException {
+        long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
+        long remainingNanos = nanos;
+        long startNanos = System.nanoTime();
+        int n;
+        do {
+            park(fd, Net.POLLOUT, remainingNanos);
+            n = Net.pollConnectNow(fd);
+            if (n == 0) {
+                remainingNanos = nanos - (System.nanoTime() - startNanos);
+                if (remainingNanos <= 0) {
+                    throw new SocketTimeoutException("Connect timed out");
+                }
+            }
+        } while (n == 0 && isOpen());
+        return n;
+    }
+
+    /**
+     * Attempts to establish a connection to the given socket address with a
+     * timeout. Closes the socket if connection cannot be established.
+     * @throws IllegalArgumentException if the address is not an InetSocketAddress
+     * @throws UnknownHostException if the InetSocketAddress is not resolved
+     * @throws IOException if the connection cannot be established
+     */
+    private void implConnect(SocketAddress remote, int millis) throws IOException {
+        if (!(remote instanceof InetSocketAddress))
+            throw new IllegalArgumentException("Unsupported address type");
+        InetSocketAddress isa = (InetSocketAddress) remote;
+        if (isa.isUnresolved()) {
+            throw new UnknownHostException(isa.getHostName());
+        }
+
+        InetAddress address = isa.getAddress();
+        if (address.isAnyLocalAddress())
+            address = InetAddress.getLocalHost();
+        int port = isa.getPort();
+
+        ReentrantLock connectLock = readLock;
+        try {
+            connectLock.lock();
+            try {
+                boolean connected = false;
+                FileDescriptor fd = beginConnect(address, port);
+                try {
+                    configureNonBlockingIfNeeded(fd, millis);
+                    int n = Net.connect(fd, address, port);
+                    if (IOStatus.okayToRetry(n) && isOpen()) {
+                        if (millis > 0) {
+                            // finish connect with timeout
+                            n = timedFinishConnect(fd, millis);
+                        } else {
+                            // finish connect, no timeout
+                            do {
+                                park(fd, Net.POLLOUT);
+                                n = Net.pollConnectNow(fd);
+                            } while (n == 0 && isOpen());
+                        }
+                    }
+                    connected = (n > 0) && isOpen();
+                } finally {
+                    endConnect(connected);
+                }
+            } finally {
+                connectLock.unlock();
+            }
+        } catch (IOException ioe) {
+            close();
+            throw SocketExceptions.of(ioe, isa);
+        }
+    }
+
+    @Override
+    protected void connect(String host, int port) throws IOException {
+        implConnect(new InetSocketAddress(host, port), timeout);
+    }
+
+    @Override
+    protected void connect(InetAddress address, int port) throws IOException {
+        implConnect(new InetSocketAddress(address, port), timeout);
+    }
+
+    @Override
+    protected void connect(SocketAddress address, int timeout) throws IOException {
+        implConnect(address, timeout);
+    }
+
+    @Override
+    protected void bind(InetAddress host, int port) throws IOException {
+        synchronized (stateLock) {
+            ensureOpen();
+            if (localport != 0)
+                throw new SocketException("Already bound");
+            NetHooks.beforeTcpBind(fd, host, port);
+            Net.bind(fd, host, port);
+            // set the address field to the given host address to keep
+            // compatibility with PlainSocketImpl. When binding to 0.0.0.0
+            // then the actual local address will be ::0 when IPv6 is enabled.
+            address = host;
+            localport = Net.localAddress(fd).getPort();
+        }
+    }
+
+    @Override
+    protected void listen(int backlog) throws IOException {
+        synchronized (stateLock) {
+            ensureOpen();
+            if (localport == 0)
+                throw new SocketException("Not bound");
+            Net.listen(fd, backlog < 1 ? 50 : backlog);
+        }
+    }
+
+    /**
+     * Marks the beginning of an accept operation that might block.
+     * @throws SocketException if the socket is closed
+     */
+    private FileDescriptor beginAccept() throws SocketException {
+        synchronized (stateLock) {
+            ensureOpen();
+            if (!stream)
+                throw new SocketException("Not a stream socket");
+            if (localport == 0)
+                throw new SocketException("Not bound");
+            readerThread = NativeThread.current();
+            return fd;
+        }
+    }
+
+    /**
+     * Marks the end of an accept operation that may have blocked.
+     * @throws SocketException is the socket is closed
+     */
+    private void endAccept(boolean completed) throws SocketException {
+        synchronized (stateLock) {
+            int state = this.state;
+            readerThread = 0;
+            if (state == ST_CLOSING)
+                stateLock.notifyAll();
+            if (!completed && state >= ST_CLOSING)
+                throw new SocketException("Socket closed");
+        }
+    }
+
+    /**
+     * Accepts a new connection with a timeout.
+     * @throws SocketTimeoutException if the accept timeout elapses
+     */
+    private int timedAccept(FileDescriptor fd,
+                            FileDescriptor newfd,
+                            InetSocketAddress[] isaa,
+                            int millis)
+        throws IOException
+    {
+        assert nonBlocking;
+        long nanos = NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS);
+        long remainingNanos = nanos;
+        long startNanos = System.nanoTime();
+        int n;
+        do {
+            park(fd, Net.POLLIN, remainingNanos);
+            n = ServerSocketChannelImpl.accept0(fd, newfd, isaa);
+            if (n == IOStatus.UNAVAILABLE) {
+                remainingNanos = nanos - (System.nanoTime() - startNanos);
+                if (remainingNanos <= 0) {
+                    throw new SocketTimeoutException("Accept timed out");
+                }
+            }
+        } while (n == IOStatus.UNAVAILABLE && isOpen());
+        return n;
+    }
+
+    /**
+     * Accepts a new connection so that the given SocketImpl is connected to
+     * the peer.
+     */
+    @Override
+    protected void accept(SocketImpl si) throws IOException {
+        // accept a connection
+        FileDescriptor newfd = new FileDescriptor();
+        InetSocketAddress[] isaa = new InetSocketAddress[1];
+
+        ReentrantLock acceptLock = readLock;
+        acceptLock.lock();
+        try {
+            int n = 0;
+            FileDescriptor fd = beginAccept();
+            try {
+                int timeout = this.timeout;
+                configureNonBlockingIfNeeded(fd, timeout);
+                n = ServerSocketChannelImpl.accept0(fd, newfd, isaa);
+                if (IOStatus.okayToRetry(n) && isOpen()) {
+                    if (timeout > 0) {
+                        // accept with timeout
+                        n = timedAccept(fd, newfd, isaa, timeout);
+                    } else {
+                        // accept, no timeout
+                        do {
+                            park(fd, Net.POLLIN);
+                            n = ServerSocketChannelImpl.accept0(fd, newfd, isaa);
+                        } while (IOStatus.okayToRetry(n) && isOpen());
+                    }
+                }
+            } finally {
+                endAccept(n > 0);
+                assert IOStatus.check(n);
+            }
+        } finally {
+            acceptLock.unlock();
+        }
+
+        // get local address and configure accepted socket to blocking mode
+        InetSocketAddress localAddress;
+        try {
+            localAddress = Net.localAddress(newfd);
+            IOUtil.configureBlocking(newfd, true);
+        } catch (IOException ioe) {
+            nd.close(newfd);
+            throw ioe;
+        }
+
+        // set the fields
+        InetSocketAddress remoteAddress = isaa[0];
+        if (si instanceof NioSocketImpl) {
+            NioSocketImpl nsi = (NioSocketImpl) si;
+            synchronized (nsi.stateLock) {
+                nsi.fd = newfd;
+                nsi.stream = true;
+                nsi.closer = FileDescriptorCloser.create(nsi);
+                nsi.localport = localAddress.getPort();
+                nsi.address = remoteAddress.getAddress();
+                nsi.port = remoteAddress.getPort();
+                nsi.state = ST_CONNECTED;
+            }
+        } else {
+            // set fields in foreign impl
+            setSocketImplFields(si, newfd,
+                                localAddress.getPort(),
+                                remoteAddress.getAddress(),
+                                remoteAddress.getPort());
+        }
+    }
+
+    @Override
+    protected InputStream getInputStream() {
+        return new InputStream() {
+            @Override
+            public int read() throws IOException {
+                byte[] a = new byte[1];
+                int n = read(a, 0, 1);
+                return (n > 0) ? (a[0] & 0xff) : -1;
+            }
+            @Override
+            public int read(byte[] b, int off, int len) throws IOException {
+                return NioSocketImpl.this.read(b, off, len);
+            }
+            @Override
+            public int available() throws IOException {
+                return NioSocketImpl.this.available();
+            }
+            @Override
+            public void close() throws IOException {
+                NioSocketImpl.this.close();
+            }
+        };
+    }
+
+    @Override
+    protected OutputStream getOutputStream() {
+        return new OutputStream() {
+            @Override
+            public void write(int b) throws IOException {
+                byte[] a = new byte[]{(byte) b};
+                write(a, 0, 1);
+            }
+            @Override
+            public void write(byte[] b, int off, int len) throws IOException {
+                NioSocketImpl.this.write(b, off, len);
+            }
+            @Override
+            public void close() throws IOException {
+                NioSocketImpl.this.close();
+            }
+        };
+    }
+
+    @Override
+    protected int available() throws IOException {
+        readLock.lock();
+        try {
+            ensureOpenAndConnected();
+            if (isInputClosed) {
+                return 0;
+            } else {
+                return Net.available(fd);
+            }
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    /**
+     * Closes the socket, signalling and waiting for blocking I/O operations
+     * to complete.
+     */
+    @Override
+    protected void close() throws IOException {
+        boolean interrupted = false;
+
+        synchronized (stateLock) {
+            int state = this.state;
+            if (state >= ST_CLOSING)
+                return;
+            if (state == ST_NEW) {
+                // stillborn
+                this.state = ST_CLOSED;
+                return;
+            }
+            this.state = ST_CLOSING;
+            assert fd != null && closer != null;
+
+            // shutdown output when linger interval not set
+            try {
+                var SO_LINGER = StandardSocketOptions.SO_LINGER;
+                if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
+                    Net.shutdown(fd, Net.SHUT_WR);
+                }
+            } catch (IOException ignore) { }
+
+            // interrupt and wait for kernel threads to complete I/O operations
+            long reader = readerThread;
+            long writer = writerThread;
+            if (reader != 0 || writer != 0) {
+                nd.preClose(fd);
+
+                if (reader != 0)
+                    NativeThread.signal(reader);
+                if (writer != 0)
+                    NativeThread.signal(writer);
+
+                // wait for blocking I/O operations to end
+                while (readerThread != 0 || writerThread != 0) {
+                    try {
+                        stateLock.wait();
+                    } catch (InterruptedException e) {
+                        interrupted = true;
+                    }
+                }
+            }
+
+            // close file descriptor
+            try {
+                closer.run();
+            } finally {
+                this.state = ST_CLOSED;
+            }
+        }
+
+        // restore interrupt status
+        if (interrupted)
+            Thread.currentThread().interrupt();
+    }
+
+    @Override
+    protected Set<SocketOption<?>> supportedOptions() {
+        Set<SocketOption<?>> options = new HashSet<>();
+        options.addAll(super.supportedOptions());
+        if (server) {
+            options.addAll(ExtendedSocketOptions.serverSocketOptions());
+        } else {
+            options.addAll(ExtendedSocketOptions.clientSocketOptions());
+        }
+        if (Net.isReusePortAvailable())
+            options.add(StandardSocketOptions.SO_REUSEPORT);
+        return Collections.unmodifiableSet(options);
+    }
+
+    @Override
+    protected <T> void setOption(SocketOption<T> opt, T value) throws IOException {
+        if (!supportedOptions().contains(opt))
+            throw new UnsupportedOperationException("'" + opt + "' not supported");
+        synchronized (stateLock) {
+            ensureOpen();
+            if (opt == StandardSocketOptions.IP_TOS) {
+                // maps to IP_TOS or IPV6_TCLASS
+                int i = (int) value;
+                Net.setSocketOption(fd, family(), opt, i);
+                trafficClass = i;
+            } else if (opt == StandardSocketOptions.SO_REUSEADDR) {
+                boolean b = (boolean) value;
+                if (Net.useExclusiveBind()) {
+                    isReuseAddress = b;
+                } else {
+                    Net.setSocketOption(fd, opt, b);
+                }
+            } else {
+                // option does not need special handling
+                Net.setSocketOption(fd, opt, value);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T> T getOption(SocketOption<T> opt) throws IOException {
+        if (!supportedOptions().contains(opt))
+            throw new UnsupportedOperationException("'" + opt + "' not supported");
+        synchronized (stateLock) {
+            ensureOpen();
+            if (opt == StandardSocketOptions.IP_TOS) {
+                return (T) Integer.valueOf(trafficClass);
+            } else if (opt == StandardSocketOptions.SO_REUSEADDR) {
+                if (Net.useExclusiveBind()) {
+                    return (T) Boolean.valueOf(isReuseAddress);
+                } else {
+                    return (T) Net.getSocketOption(fd, opt);
+                }
+            } else {
+                // option does not need special handling
+                return (T) Net.getSocketOption(fd, opt);
+            }
+        }
+    }
+
+    private boolean booleanValue(Object value, String desc) throws SocketException {
+        if (!(value instanceof Boolean))
+            throw new SocketException("Bad value for " + desc);
+        return (boolean) value;
+    }
+
+    private int intValue(Object value, String desc) throws SocketException {
+        if (!(value instanceof Integer))
+            throw new SocketException("Bad value for " + desc);
+        return (int) value;
+    }
+
+    @Override
+    public void setOption(int opt, Object value) throws SocketException {
+        synchronized (stateLock) {
+            ensureOpen();
+            try {
+                switch (opt) {
+                case SO_LINGER: {
+                    // the value is "false" to disable, or linger interval to enable
+                    int i;
+                    if (value instanceof Boolean && ((boolean) value) == false) {
+                        i = -1;
+                    } else {
+                        i = intValue(value, "SO_LINGER");
+                    }
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_LINGER, i);
+                    break;
+                }
+                case SO_TIMEOUT: {
+                    int i = intValue(value, "SO_TIMEOUT");
+                    if (i < 0)
+                        throw new IllegalArgumentException("timeout < 0");
+                    timeout = i;
+                    break;
+                }
+                case IP_TOS: {
+                    int i = intValue(value, "IP_TOS");
+                    Net.setSocketOption(fd, family(), StandardSocketOptions.IP_TOS, i);
+                    trafficClass = i;
+                    break;
+                }
+                case TCP_NODELAY: {
+                    boolean b = booleanValue(value, "TCP_NODELAY");
+                    Net.setSocketOption(fd, StandardSocketOptions.TCP_NODELAY, b);
+                    break;
+                }
+                case SO_SNDBUF: {
+                    int i = intValue(value, "SO_SNDBUF");
+                    if (i <= 0)
+                        throw new SocketException("SO_SNDBUF <= 0");
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_SNDBUF, i);
+                    break;
+                }
+                case SO_RCVBUF: {
+                    int i = intValue(value, "SO_RCVBUF");
+                    if (i <= 0)
+                        throw new SocketException("SO_RCVBUF <= 0");
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_RCVBUF, i);
+                    break;
+                }
+                case SO_KEEPALIVE: {
+                    boolean b = booleanValue(value, "SO_KEEPALIVE");
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_KEEPALIVE, b);
+                    break;
+                }
+                case SO_OOBINLINE: {
+                    boolean b = booleanValue(value, "SO_OOBINLINE");
+                    Net.setSocketOption(fd, ExtendedSocketOption.SO_OOBINLINE, b);
+                    break;
+                }
+                case SO_REUSEADDR: {
+                    boolean b = booleanValue(value, "SO_REUSEADDR");
+                    if (Net.useExclusiveBind()) {
+                        isReuseAddress = b;
+                    } else {
+                        Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEADDR, b);
+                    }
+                    break;
+                }
+                case SO_REUSEPORT: {
+                    if (!Net.isReusePortAvailable())
+                        throw new SocketException("SO_REUSEPORT not supported");
+                    boolean b = booleanValue(value, "SO_REUSEPORT");
+                    Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEPORT, b);
+                    break;
+                }
+                default:
+                    throw new SocketException("Unknown option " + opt);
+                }
+            } catch (SocketException e) {
+                throw e;
+            } catch (IllegalArgumentException | IOException e) {
+                throw new SocketException(e.getMessage());
+            }
+        }
+    }
+
+    @Override
+    public Object getOption(int opt) throws SocketException {
+        synchronized (stateLock) {
+            ensureOpen();
+            try {
+                switch (opt) {
+                case SO_TIMEOUT:
+                    return timeout;
+                case TCP_NODELAY:
+                    return Net.getSocketOption(fd, StandardSocketOptions.TCP_NODELAY);
+                case SO_OOBINLINE:
+                    return Net.getSocketOption(fd, ExtendedSocketOption.SO_OOBINLINE);
+                case SO_LINGER: {
+                    // return "false" when disabled, linger interval when enabled
+                    int i = (int) Net.getSocketOption(fd, StandardSocketOptions.SO_LINGER);
+                    if (i == -1) {
+                        return Boolean.FALSE;
+                    } else {
+                        return i;
+                    }
+                }
+                case SO_REUSEADDR:
+                    if (Net.useExclusiveBind()) {
+                        return isReuseAddress;
+                    } else {
+                        return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEADDR);
+                    }
+                case SO_BINDADDR:
+                    return Net.localAddress(fd).getAddress();
+                case SO_SNDBUF:
+                    return Net.getSocketOption(fd, StandardSocketOptions.SO_SNDBUF);
+                case SO_RCVBUF:
+                    return Net.getSocketOption(fd, StandardSocketOptions.SO_RCVBUF);
+                case IP_TOS:
+                    return trafficClass;
+                case SO_KEEPALIVE:
+                    return Net.getSocketOption(fd, StandardSocketOptions.SO_KEEPALIVE);
+                case SO_REUSEPORT:
+                    if (!Net.isReusePortAvailable())
+                        throw new SocketException("SO_REUSEPORT not supported");
+                    return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEPORT);
+                default:
+                    throw new SocketException("Unknown option " + opt);
+                }
+            } catch (SocketException e) {
+                throw e;
+            } catch (IllegalArgumentException | IOException e) {
+                throw new SocketException(e.getMessage());
+            }
+        }
+    }
+
+    @Override
+    protected void shutdownInput() throws IOException {
+        synchronized (stateLock) {
+            ensureOpenAndConnected();
+            if (!isInputClosed) {
+                Net.shutdown(fd, Net.SHUT_RD);
+                isInputClosed = true;
+            }
+        }
+    }
+
+    @Override
+    protected void shutdownOutput() throws IOException {
+        synchronized (stateLock) {
+            ensureOpenAndConnected();
+            if (!isOutputClosed) {
+                Net.shutdown(fd, Net.SHUT_WR);
+                isOutputClosed = true;
+            }
+        }
+    }
+
+    @Override
+    protected boolean supportsUrgentData() {
+        return true;
+    }
+
+    @Override
+    protected void sendUrgentData(int data) throws IOException {
+        writeLock.lock();
+        try {
+            int n = 0;
+            FileDescriptor fd = beginWrite();
+            try {
+                do {
+                    n = Net.sendOOB(fd, (byte) data);
+                } while (n == IOStatus.INTERRUPTED && isOpen());
+                if (n == IOStatus.UNAVAILABLE) {
+                    throw new SocketException("No buffer space available");
+                }
+            } finally {
+                endWrite(n > 0);
+            }
+        } finally {
+            writeLock.unlock();
+        }
+    }
+
+    /**
+     * A task that closes a SocketImpl's file descriptor. The task runs when the
+     * SocketImpl is explicitly closed and when the SocketImpl becomes phantom
+     * reachable.
+     */
+    private static class FileDescriptorCloser implements Runnable {
+        private static final VarHandle CLOSED;
+        static {
+            try {
+                MethodHandles.Lookup l = MethodHandles.lookup();
+                CLOSED = l.findVarHandle(FileDescriptorCloser.class,
+                                         "closed",
+                                         boolean.class);
+            } catch (Exception e) {
+                throw new InternalError(e);
+            }
+        }
+
+        private final FileDescriptor fd;
+        private final boolean stream;
+        private volatile boolean closed;
+
+        FileDescriptorCloser(FileDescriptor fd, boolean stream) {
+            this.fd = fd;
+            this.stream = stream;
+        }
+
+        static FileDescriptorCloser create(NioSocketImpl impl) {
+            assert Thread.holdsLock(impl.stateLock);
+            var closer = new FileDescriptorCloser(impl.fd, impl.stream);
+            CleanerFactory.cleaner().register(impl, closer);
+            return closer;
+        }
+
+        @Override
+        public void run() {
+            if (CLOSED.compareAndSet(this, false, true)) {
+                try {
+                    nd.close(fd);
+                } catch (IOException ioe) {
+                    throw new RuntimeException(ioe);
+                } finally {
+                    if (!stream) {
+                        // decrement
+                        ResourceManager.afterUdpClose();
+                    }
+                }
+            }
+        }
+
+        boolean disable() {
+            return CLOSED.compareAndSet(this, false, true);
+        }
+    }
+
+    /**
+     * Returns the socket protocol family.
+     */
+    private static ProtocolFamily family() {
+        if (Net.isIPv6Available()) {
+            return StandardProtocolFamily.INET6;
+        } else {
+            return StandardProtocolFamily.INET;
+        }
+    }
+
+    /**
+     * Sets the SocketImpl fields to the given values.
+     */
+    private static void setSocketImplFields(SocketImpl si,
+                                            FileDescriptor fd,
+                                            int localport,
+                                            InetAddress address,
+                                            int port)
+    {
+        PrivilegedExceptionAction<Void> pa = () -> {
+            setSocketImplField(si, "fd", fd);
+            setSocketImplField(si, "localport", localport);
+            setSocketImplField(si, "address", address);
+            setSocketImplField(si, "port", port);
+            return null;
+        };
+        try {
+            AccessController.doPrivileged(pa);
+        } catch (PrivilegedActionException pae) {
+            throw new InternalError(pae);
+        }
+    }
+
+    private static void setSocketImplField(SocketImpl si, String name, Object value)
+        throws Exception
+    {
+        Field field = SocketImpl.class.getDeclaredField(name);
+        field.setAccessible(true);
+        field.set(si, value);
+    }
+
+    /**
+     * Throws InternalError if the given expression is not true.
+     */
+    private static void guarantee(boolean expr) {
+        if (!expr) throw new InternalError();
+    }
+}
--- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Sat Mar 09 12:54:20 2019 +0000
@@ -529,7 +529,7 @@
     // Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no
     // connections are pending) or IOStatus.INTERRUPTED.
     //
-    private native int accept0(FileDescriptor ssfd,
+    static native int accept0(FileDescriptor ssfd,
                                FileDescriptor newfd,
                                InetSocketAddress[] isaa)
         throws IOException;
--- a/src/java.base/unix/classes/sun/nio/ch/NativeThread.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/unix/classes/sun/nio/ch/NativeThread.java	Sat Mar 09 12:54:20 2019 +0000
@@ -25,7 +25,6 @@
 
 package sun.nio.ch;
 
-
 // Signalling operations on native threads
 //
 // On some operating systems (e.g., Linux), closing a channel while another
@@ -33,23 +32,35 @@
 // thread to be released.  This class provides access to the native threads
 // upon which Java threads are built, and defines a simple signal mechanism
 // that can be used to release a native thread from a blocking I/O operation.
-// On systems that do not require this type of signalling, the current() method
-// always returns -1 and the signal(long) method has no effect.
 
+import jdk.internal.access.JavaLangAccess;
+import jdk.internal.access.SharedSecrets;
 
 public class NativeThread {
+    private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 
-    // Returns an opaque token representing the native thread underlying the
-    // invoking Java thread.  On systems that do not require signalling, this
-    // method always returns -1.
-    //
-    public static native long current();
+    /**
+     * Returns the current thread's ID.
+     */
+    public static long current() {
+        long tid = JLA.nativeTid();
+        if (tid == 0) {
+            tid = current0();
+            JLA.setNativeTid(tid);
+        }
+        return tid;
+    }
 
-    // Signals the given native thread so as to release it from a blocking I/O
-    // operation.  On systems that do not require signalling, this method has
-    // no effect.
-    //
-    public static native void signal(long nt);
+    /**
+     * Signals the given thread.
+     */
+    public static void signal(long tid) {
+        signal0(tid);
+    }
+
+    private static native long current0();
+
+    private static native void signal0(long tid);
 
     private static native void init();
 
--- a/src/java.base/unix/classes/sun/nio/ch/SocketDispatcher.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/unix/classes/sun/nio/ch/SocketDispatcher.java	Sat Mar 09 12:54:20 2019 +0000
@@ -34,9 +34,22 @@
  */
 
 class SocketDispatcher extends NativeDispatcher {
+    private final boolean detectConnectionReset;
+
+    SocketDispatcher(boolean detectConnectionReset) {
+        this.detectConnectionReset = detectConnectionReset;
+    }
+
+    SocketDispatcher() {
+        this(false);
+    }
 
     int read(FileDescriptor fd, long address, int len) throws IOException {
-        return FileDispatcherImpl.read0(fd, address, len);
+        if (detectConnectionReset) {
+            return read0(fd, address, len);
+        } else {
+            return FileDispatcherImpl.read0(fd, address, len);
+        }
     }
 
     long readv(FileDescriptor fd, long address, int len) throws IOException {
@@ -58,4 +71,20 @@
     void preClose(FileDescriptor fd) throws IOException {
         FileDispatcherImpl.preClose0(fd);
     }
+
+    // -- Native methods --
+
+    /**
+     * Reads up to len bytes from a socket with special handling for "connection
+     * reset".
+     *
+     * @throws sun.net.ConnectionResetException if connection reset is detected
+     * @throws IOException if another I/O error occurs
+     */
+    private static native int read0(FileDescriptor fd, long address, int len)
+        throws IOException;
+
+    static {
+        IOUtil.load();
+    }
 }
--- a/src/java.base/unix/native/libnio/ch/NativeThread.c	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/unix/native/libnio/ch/NativeThread.c	Sat Mar 09 12:54:20 2019 +0000
@@ -77,7 +77,7 @@
 }
 
 JNIEXPORT jlong JNICALL
-Java_sun_nio_ch_NativeThread_current(JNIEnv *env, jclass cl)
+Java_sun_nio_ch_NativeThread_current0(JNIEnv *env, jclass cl)
 {
 #ifdef __solaris__
     return (jlong)thr_self();
@@ -87,7 +87,7 @@
 }
 
 JNIEXPORT void JNICALL
-Java_sun_nio_ch_NativeThread_signal(JNIEnv *env, jclass cl, jlong thread)
+Java_sun_nio_ch_NativeThread_signal0(JNIEnv *env, jclass cl, jlong thread)
 {
     int ret;
 #ifdef __solaris__
--- a/src/java.base/unix/native/libnio/ch/Net.c	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/unix/native/libnio/ch/Net.c	Sat Mar 09 12:54:20 2019 +0000
@@ -830,6 +830,7 @@
             break;
         case EADDRINUSE:  /* Fall through */
         case EADDRNOTAVAIL:
+        case EACCES:
             xn = JNU_JAVANETPKG "BindException";
             break;
         default:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/unix/native/libnio/ch/SocketDispatcher.c	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,49 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+ #include <sys/types.h>
+ #include <unistd.h>
+
+ #include "jni.h"
+ #include "jni_util.h"
+ #include "jlong.h"
+ #include "nio.h"
+ #include "nio_util.h"
+ #include "sun_nio_ch_SocketDispatcher.h"
+
+ JNIEXPORT jint JNICALL
+ Java_sun_nio_ch_SocketDispatcher_read0(JNIEnv *env, jclass clazz,
+                                        jobject fdo, jlong address, jint len)
+ {
+     jint fd = fdval(env, fdo);
+     void *buf = (void *)jlong_to_ptr(address);
+     jint n = read(fd, buf, len);
+     if ((n == -1) && (errno == ECONNRESET || errno == EPIPE)) {
+         JNU_ThrowByName(env, "sun/net/ConnectionResetException", "Connection reset");
+         return IOS_THROWN;
+     } else {
+         return convertReturnVal(env, n, JNI_TRUE);
+     }
+ }
--- a/src/java.base/windows/classes/sun/nio/ch/SocketDispatcher.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/windows/classes/sun/nio/ch/SocketDispatcher.java	Sat Mar 09 12:54:20 2019 +0000
@@ -32,12 +32,11 @@
  * for read and write operations.
  */
 
-class SocketDispatcher extends NativeDispatcher
-{
+class SocketDispatcher extends NativeDispatcher {
 
-    static {
-        IOUtil.load();
-    }
+    SocketDispatcher() { }
+
+    SocketDispatcher(boolean ignore) { }
 
     int read(FileDescriptor fd, long address, int len) throws IOException {
         return read0(fd, address, len);
@@ -63,7 +62,8 @@
         close0(fd);
     }
 
-    //-- Native methods
+    // -- Native methods --
+
     static native int read0(FileDescriptor fd, long address, int len)
         throws IOException;
 
@@ -79,4 +79,8 @@
     static native void preClose0(FileDescriptor fd) throws IOException;
 
     static native void close0(FileDescriptor fd) throws IOException;
+
+    static {
+        IOUtil.load();
+    }
 }
--- a/src/java.base/windows/native/libnio/ch/SocketDispatcher.c	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/java.base/windows/native/libnio/ch/SocketDispatcher.c	Sat Mar 09 12:54:20 2019 +0000
@@ -72,7 +72,11 @@
         if (theErr == WSAEWOULDBLOCK) {
             return IOS_UNAVAILABLE;
         }
-        JNU_ThrowIOExceptionWithLastError(env, "Read failed");
+        if (theErr == WSAECONNRESET) {
+            JNU_ThrowIOException(env, "Connection reset by peer");
+        } else {
+            JNU_ThrowIOExceptionWithLastError(env, "Read failed");
+        }
         return IOS_THROWN;
     }
 
@@ -128,7 +132,11 @@
         if (theErr == WSAEWOULDBLOCK) {
             return IOS_UNAVAILABLE;
         }
-        JNU_ThrowIOExceptionWithLastError(env, "Vector read failed");
+        if (theErr == WSAECONNRESET) {
+            JNU_ThrowIOException(env, "Connection reset by peer");
+        } else {
+            JNU_ThrowIOExceptionWithLastError(env, "Vector read failed");
+        }
         return IOS_THROWN;
     }
 
@@ -174,7 +182,11 @@
                if (theErr == WSAEWOULDBLOCK) {
                    return IOS_UNAVAILABLE;
                }
-               JNU_ThrowIOExceptionWithLastError(env, "Write failed");
+               if (theErr == WSAECONNRESET) {
+                   JNU_ThrowIOException(env, "Connection reset by peer");
+               } else {
+                   JNU_ThrowIOExceptionWithLastError(env, "Write failed");
+               }
                return IOS_THROWN;
             }
         }
@@ -256,7 +268,11 @@
         if (theErr == WSAEWOULDBLOCK) {
             return IOS_UNAVAILABLE;
         }
-        JNU_ThrowIOExceptionWithLastError(env, "Vector write failed");
+        if (theErr == WSAECONNRESET) {
+            JNU_ThrowIOException(env, "Connection reset by peer");
+        } else {
+            JNU_ThrowIOExceptionWithLastError(env, "Vector write failed");
+        }
         return IOS_THROWN;
     }
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketInputStreamInstrumentor.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketInputStreamInstrumentor.java	Sat Mar 09 12:54:20 2019 +0000
@@ -27,15 +27,14 @@
 
 import java.io.IOException;
 import java.net.InetAddress;
+import java.net.Socket;
 
 import jdk.jfr.events.SocketReadEvent;
 
 /**
  * See {@link JITracer} for an explanation of this code.
  */
-@JIInstrumentationTarget("java.net.SocketInputStream")
-@JITypeMapping(from = "jdk.jfr.internal.instrument.SocketInputStreamInstrumentor$AbstractPlainSocketImpl",
-            to = "java.net.AbstractPlainSocketImpl")
+@JIInstrumentationTarget("java.net.Socket$SocketInputStream")
 final class SocketInputStreamInstrumentor {
 
     private SocketInputStreamInstrumentor() {
@@ -43,30 +42,28 @@
 
     @SuppressWarnings("deprecation")
     @JIInstrumentationMethod
-    int read(byte b[], int off, int length, int timeout) throws IOException {
+    public int read(byte b[], int off, int length) throws IOException {
         SocketReadEvent event = SocketReadEvent.EVENT.get();
         if (!event.isEnabled()) {
-            return read(b, off, length, timeout);
+            return read(b, off, length);
         }
         int bytesRead = 0;
         try {
             event.begin();
-            bytesRead = read(b, off, length, timeout);
+            bytesRead = read(b, off, length);
         } finally {
             event.end();
             if (event.shouldCommit()) {
-                String hostString  = impl.address.toString();
-                int delimiterIndex = hostString.lastIndexOf('/');
-
-                event.host      = hostString.substring(0, delimiterIndex);
-                event.address   = hostString.substring(delimiterIndex + 1);
-                event.port      = impl.port;
+                InetAddress remote = parent.getInetAddress();
+                event.host = remote.getHostName();
+                event.address = remote.getHostAddress();
+                event.port = parent.getPort();
                 if (bytesRead < 0) {
                     event.endOfStream = true;
                 } else {
                     event.bytesRead = bytesRead;
                 }
-                event.timeout   = timeout;
+                event.timeout = parent.getSoTimeout();
 
                 event.commit();
                 event.reset();
@@ -75,14 +72,6 @@
         return bytesRead;
     }
 
-    private AbstractPlainSocketImpl impl = null;
-
-    void silenceFindBugsUnwrittenField(InetAddress dummy) {
-        impl.address = dummy;
-    }
-
-    static class AbstractPlainSocketImpl {
-        InetAddress address;
-        int port;
-    }
+    // private field in java.net.Socket$SocketInputStream
+    private Socket parent;
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketOutputStreamInstrumentor.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketOutputStreamInstrumentor.java	Sat Mar 09 12:54:20 2019 +0000
@@ -27,15 +27,14 @@
 
 import java.io.IOException;
 import java.net.InetAddress;
+import java.net.Socket;
 
 import jdk.jfr.events.SocketWriteEvent;
 
 /**
  * See {@link JITracer} for an explanation of this code.
  */
-@JIInstrumentationTarget("java.net.SocketOutputStream")
-@JITypeMapping(from = "jdk.jfr.internal.instrument.SocketOutputStreamInstrumentor$AbstractPlainSocketImpl",
-            to = "java.net.AbstractPlainSocketImpl")
+@JIInstrumentationTarget("java.net.Socket$SocketOutputStream")
 final class SocketOutputStreamInstrumentor {
 
     private SocketOutputStreamInstrumentor() {
@@ -43,27 +42,25 @@
 
     @SuppressWarnings("deprecation")
     @JIInstrumentationMethod
-    private void socketWrite(byte b[], int off, int len) throws IOException {
+    public void write(byte b[], int off, int len) throws IOException {
         SocketWriteEvent event = SocketWriteEvent.EVENT.get();
         if (!event.isEnabled()) {
-            socketWrite(b, off, len);
+            write(b, off, len);
             return;
         }
         int bytesWritten = 0;
         try {
             event.begin();
-            socketWrite(b, off, len);
+            write(b, off, len);
             bytesWritten = len;
         } finally {
             event.end() ;
             if (event.shouldCommit()) {
-                String hostString  = impl.address.toString();
-                int delimiterIndex = hostString.lastIndexOf('/');
-
-                event.host         = hostString.substring(0, delimiterIndex);
-                event.address      = hostString.substring(delimiterIndex + 1);
-                event.port         = impl.port;
-                event.bytesWritten = bytesWritten < 0 ? 0 : bytesWritten;
+                InetAddress remote = parent.getInetAddress();
+                event.host = remote.getHostName();
+                event.address = remote.getHostAddress();
+                event.port = parent.getPort();
+                event.bytesWritten = bytesWritten;
 
                 event.commit();
                 event.reset();
@@ -71,14 +68,6 @@
         }
     }
 
-    private AbstractPlainSocketImpl impl = null;
-
-    void silenceFindBugsUnwrittenField(InetAddress dummy) {
-        impl.address = dummy;
-    }
-
-    static class AbstractPlainSocketImpl {
-        InetAddress address;
-        int port;
-    }
+    // private field in java.net.Socket$SocketOutputStream
+    private Socket parent;
 }
--- a/test/jdk/ProblemList.txt	Fri Mar 08 17:45:40 2019 -0500
+++ b/test/jdk/ProblemList.txt	Sat Mar 09 12:54:20 2019 +0000
@@ -562,6 +562,9 @@
 
 java/net/ServerSocket/AcceptInheritHandle.java                  8211854 aix-ppc64
 
+java/net/Inet6Address/B6206527.java                             8216417 macosx-all
+java/net/ipv6tests/B6521014.java                                8216417 macosx-all
+
 ############################################################################
 
 # jdk_nio
@@ -871,3 +874,5 @@
 
 jdk/jfr/event/io/TestInstrumentation.java                       8202142    generic-all
 jdk/jfr/api/recording/event/TestPeriod.java                     8215890    generic-all
+
+jdk/jfr/event/io/EvilInstrument.java                            0000000    generic-all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/Socket/AsyncShutdown.java	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,138 @@
+/*
+ * 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
+ * @requires (os.family == "linux" | os.family == "mac")
+ * @run testng AsyncShutdown
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl AsyncShutdown
+ * @summary Test shutdownInput/shutdownOutput with threads blocked in read/write
+ */
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class AsyncShutdown {
+
+    public void testShutdownInput1() throws IOException {
+        withConnection((s1, s2) -> {
+            scheduleShutdownInput(s1, 2000);
+            int n = s1.getInputStream().read();
+            assertTrue(n == -1);
+        });
+    }
+
+    public void testShutdownInput2() throws IOException {
+        withConnection((s1, s2) -> {
+            scheduleShutdownInput(s1, 2000);
+            s1.setSoTimeout(30*1000);
+            int n = s1.getInputStream().read();
+            assertTrue(n == -1);
+        });
+    }
+
+    public void testShutdownOutput1() throws IOException {
+        withConnection((s1, s2) -> {
+            scheduleShutdownOutput(s1, 2000);
+            byte[] data = new byte[128*1024];
+            try {
+                while (true) {
+                    s1.getOutputStream().write(data);
+                }
+            } catch (IOException expected) { }
+        });
+    }
+
+    public void testShutdownOutput2() throws IOException {
+        withConnection((s1, s2) -> {
+            s1.setSoTimeout(100);
+            try {
+                s1.getInputStream().read();
+                assertTrue(false);
+            } catch (SocketTimeoutException e) { }
+
+            scheduleShutdownOutput(s1, 2000);
+            byte[] data = new byte[128*1024];
+            try {
+                while (true) {
+                    s1.getOutputStream().write(data);
+                }
+            } catch (IOException expected) { }
+        });
+    }
+
+    static void scheduleShutdownInput(Socket s, long delay) {
+        schedule(() -> {
+            try {
+                s.shutdownInput();
+            } catch (IOException ioe) { }
+        }, delay);
+    }
+
+    static void scheduleShutdownOutput(Socket s, long delay) {
+        schedule(() -> {
+            try {
+                s.shutdownOutput();
+            } catch (IOException ioe) { }
+        }, delay);
+    }
+
+    static void schedule(Runnable task, long delay) {
+        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+        try {
+            executor.schedule(task, delay, TimeUnit.MILLISECONDS);
+        } finally {
+            executor.shutdown();
+        }
+    }
+
+    interface ThrowingBiConsumer<T, U> {
+        void accept(T t, U u) throws IOException;
+    }
+
+    static void withConnection(ThrowingBiConsumer<Socket, Socket> consumer)
+        throws IOException
+    {
+        Socket s1 = null;
+        Socket s2 = null;
+        try (ServerSocket ss = new ServerSocket(0)) {
+            s1 = new Socket();
+            s1.connect(ss.getLocalSocketAddress());
+            s2 = ss.accept();
+            consumer.accept(s1, s2);
+        } finally {
+            if (s1 != null) s1.close();
+            if (s2 != null) s2.close();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/Socket/ConnectionReset.java	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,219 @@
+/*
+ * 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
+ * @requires os.family != "solaris"
+ * @run testng ConnectionReset
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl ConnectionReset
+ * @summary Test behavior of read and available when a connection is reset
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class ConnectionReset {
+
+    static final int REPEAT_COUNT = 5;
+
+    /**
+     * Tests available before read when there are no bytes to read
+     */
+    public void testAvailableBeforeRead1() throws IOException {
+        System.out.println("testAvailableBeforeRead1");
+        withResetConnection(null, s -> {
+            InputStream in = s.getInputStream();
+            for (int i=0; i<REPEAT_COUNT; i++) {
+                int bytesAvailable = in.available();
+                System.out.format("available => %d%n", bytesAvailable);
+                assertTrue(bytesAvailable == 0);
+                try {
+                    int bytesRead = in.read();
+                    if (bytesRead == -1) {
+                        System.out.println("read => EOF");
+                    } else {
+                        System.out.println("read => 1 byte");
+                    }
+                    assertTrue(false);
+                } catch (IOException ioe) {
+                    System.out.format("read => %s (expected)%n", ioe);
+                }
+            }
+        });
+    }
+
+    /**
+     * Tests available before read when there are bytes to read
+     */
+    public void testAvailableBeforeRead2() throws IOException {
+        System.out.println("testAvailableBeforeRead2");
+        byte[] data = { 1, 2, 3 };
+        withResetConnection(data, s -> {
+            InputStream in = s.getInputStream();
+            int remaining = data.length;
+            for (int i=0; i<REPEAT_COUNT; i++) {
+                int bytesAvailable = in.available();
+                System.out.format("available => %d%n", bytesAvailable);
+                assertTrue(bytesAvailable <= remaining);
+                try {
+                    int bytesRead = in.read();
+                    if (bytesRead == -1) {
+                        System.out.println("read => EOF");
+                        assertTrue(false);
+                    } else {
+                        System.out.println("read => 1 byte");
+                        assertTrue(remaining > 0);
+                        remaining--;
+                    }
+                } catch (IOException ioe) {
+                    System.out.format("read => %s%n", ioe);
+                    remaining = 0;
+                }
+            }
+        });
+    }
+
+    /**
+     * Tests read before available when there are no bytes to read
+     */
+    public void testReadBeforeAvailable1() throws IOException {
+        System.out.println("testReadBeforeAvailable1");
+        withResetConnection(null, s -> {
+            InputStream in = s.getInputStream();
+            for (int i=0; i<REPEAT_COUNT; i++) {
+                try {
+                    int bytesRead = in.read();
+                    if (bytesRead == -1) {
+                        System.out.println("read => EOF");
+                    } else {
+                        System.out.println("read => 1 byte");
+                    }
+                    assertTrue(false);
+                } catch (IOException ioe) {
+                    System.out.format("read => %s (expected)%n", ioe);
+                }
+                int bytesAvailable = in.available();
+                System.out.format("available => %d%n", bytesAvailable);
+                assertTrue(bytesAvailable == 0);
+            }
+        });
+    }
+
+    /**
+     * Tests read before available when there are bytes to read
+     */
+    public void testReadBeforeAvailable2() throws IOException {
+        System.out.println("testReadBeforeAvailable2");
+        byte[] data = { 1, 2, 3 };
+        withResetConnection(data, s -> {
+            InputStream in = s.getInputStream();
+            int remaining = data.length;
+            for (int i=0; i<REPEAT_COUNT; i++) {
+                try {
+                    int bytesRead = in.read();
+                    if (bytesRead == -1) {
+                        System.out.println("read => EOF");
+                        assertTrue(false);
+                    } else {
+                        System.out.println("read => 1 byte");
+                        assertTrue(remaining > 0);
+                        remaining--;
+                    }
+                } catch (IOException ioe) {
+                    System.out.format("read => %s%n", ioe);
+                    remaining = 0;
+                }
+                int bytesAvailable = in.available();
+                System.out.format("available => %d%n", bytesAvailable);
+                assertTrue(bytesAvailable <= remaining);
+            }
+        });
+    }
+
+    /**
+     * Tests available and read on a socket closed after connection reset
+     */
+    public void testAfterClose() throws IOException {
+        System.out.println("testAfterClose");
+        withResetConnection(null, s -> {
+            InputStream in = s.getInputStream();
+            try {
+                in.read();
+                assertTrue(false);
+            } catch (IOException ioe) {
+                // expected
+            }
+            s.close();
+            try {
+                int bytesAvailable = in.available();
+                System.out.format("available => %d%n", bytesAvailable);
+                assertTrue(false);
+            } catch (IOException ioe) {
+                System.out.format("available => %s (expected)%n", ioe);
+            }
+            try {
+                int n = in.read();
+                System.out.format("read => %d%n", n);
+                assertTrue(false);
+            } catch (IOException ioe) {
+                System.out.format("read => %s (expected)%n", ioe);
+            }
+        });
+    }
+
+    interface ThrowingConsumer<T> {
+        void accept(T t) throws IOException;
+    }
+
+    /**
+     * Invokes a consumer with a Socket connected to a peer that has closed the
+     * connection with a "connection reset". The peer sends the given data bytes
+     * before closing (when data is not null).
+     */
+    static void withResetConnection(byte[] data, ThrowingConsumer<Socket> consumer)
+        throws IOException
+    {
+        var loopback = InetAddress.getLoopbackAddress();
+        try (var listener = new ServerSocket()) {
+            listener.bind(new InetSocketAddress(loopback, 0));
+            try (var socket = new Socket()) {
+                socket.connect(listener.getLocalSocketAddress());
+                try (Socket peer = listener.accept()) {
+                    if (data != null) {
+                        peer.getOutputStream().write(data);
+                    }
+                    peer.setSoLinger(true, 0);
+                }
+                consumer.accept(socket);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/Socket/Timeouts.java	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,500 @@
+/*
+ * 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
+ * @library /test/lib
+ * @build jdk.test.lib.Utils
+ * @run testng Timeouts
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl Timeouts
+ * @summary Test Socket timeouts
+ */
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ConnectException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+import jdk.test.lib.Utils;
+
+@Test
+public class Timeouts {
+
+    /**
+     * Test timed connect where connection is established
+     */
+    public void testTimedConnect1() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            try (Socket s = new Socket()) {
+                s.connect(ss.getLocalSocketAddress(), 2000);
+            }
+        }
+    }
+
+    /**
+     * Test timed connect where connection is refused
+     */
+    public void testTimedConnect2() throws IOException {
+        try (Socket s = new Socket()) {
+            SocketAddress remote = Utils.refusingEndpoint();
+            try {
+                s.connect(remote, 2000);
+            } catch (ConnectException expected) { }
+        }
+    }
+
+    /**
+     * Test connect with a timeout of Integer.MAX_VALUE
+     */
+    public void testTimedConnect3() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            try (Socket s = new Socket()) {
+                s.connect(ss.getLocalSocketAddress(), Integer.MAX_VALUE);
+            }
+        }
+    }
+
+    /**
+     * Test connect with a negative timeout. This case is not currently specified
+     * but the long standing behavior is to throw IllegalArgumentException.
+     */
+    public void testTimedConnect4() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            try (Socket s = new Socket()) {
+                try {
+                    s.connect(ss.getLocalSocketAddress(), -1);
+                    assertTrue(false);
+                } catch (IllegalArgumentException expected) { }
+            }
+        }
+    }
+
+    /**
+     * Test timed read where the read succeeds immediately
+     */
+    public void testTimedRead1() throws IOException {
+        withConnection((s1, s2) -> {
+            s1.getOutputStream().write(99);
+            s2.setSoTimeout(30*1000);
+            int b = s2.getInputStream().read();
+            assertTrue(b == 99);
+        });
+    }
+
+    /**
+     * Test timed read where the read succeeds after a delay
+     */
+    public void testTimedRead2() throws IOException {
+        withConnection((s1, s2) -> {
+            scheduleWrite(s1.getOutputStream(), 99, 2000);
+            s2.setSoTimeout(30*1000);
+            int b = s2.getInputStream().read();
+            assertTrue(b == 99);
+        });
+    }
+
+    /**
+     * Test timed read where the read times out
+     */
+    public void testTimedRead3() throws IOException {
+        withConnection((s1, s2) -> {
+            s2.setSoTimeout(2000);
+            try {
+                s2.getInputStream().read();
+                assertTrue(false);
+            } catch (SocketTimeoutException expected) { }
+        });
+    }
+
+    /**
+     * Test timed read that succeeds after a previous read has timed out
+     */
+    public void testTimedRead4() throws IOException {
+        withConnection((s1, s2) -> {
+            s2.setSoTimeout(2000);
+            try {
+                s2.getInputStream().read();
+                assertTrue(false);
+            } catch (SocketTimeoutException e) { }
+            s1.getOutputStream().write(99);
+            int b = s2.getInputStream().read();
+            assertTrue(b == 99);
+        });
+    }
+
+    /**
+     * Test timed read that succeeds after a previous read has timed out and
+     * after a short delay
+     */
+    public void testTimedRead5() throws IOException {
+        withConnection((s1, s2) -> {
+            s2.setSoTimeout(2000);
+            try {
+                s2.getInputStream().read();
+                assertTrue(false);
+            } catch (SocketTimeoutException e) { }
+            s2.setSoTimeout(30*3000);
+            scheduleWrite(s1.getOutputStream(), 99, 2000);
+            int b = s2.getInputStream().read();
+            assertTrue(b == 99);
+        });
+    }
+
+    /**
+     * Test untimed read that succeeds after a previous read has timed out
+     */
+    public void testTimedRead6() throws IOException {
+        withConnection((s1, s2) -> {
+            s2.setSoTimeout(2000);
+            try {
+                s2.getInputStream().read();
+                assertTrue(false);
+            } catch (SocketTimeoutException e) { }
+            s1.getOutputStream().write(99);
+            s2.setSoTimeout(0);
+            int b = s2.getInputStream().read();
+            assertTrue(b == 99);
+        });
+    }
+
+    /**
+     * Test untimed read that succeeds after a previous read has timed out and
+     * after a short delay
+     */
+    public void testTimedRead7() throws IOException {
+        withConnection((s1, s2) -> {
+            s2.setSoTimeout(2000);
+            try {
+                s2.getInputStream().read();
+                assertTrue(false);
+            } catch (SocketTimeoutException e) { }
+            scheduleWrite(s1.getOutputStream(), 99, 2000);
+            s2.setSoTimeout(0);
+            int b = s2.getInputStream().read();
+            assertTrue(b == 99);
+        });
+    }
+
+    /**
+     * Test async close of timed read
+     */
+    public void testTimedRead8() throws IOException {
+        withConnection((s1, s2) -> {
+            s2.setSoTimeout(30*1000);
+            scheduleClose(s2, 2000);
+            try {
+                s2.getInputStream().read();
+                assertTrue(false);
+            } catch (SocketException expected) { }
+        });
+    }
+
+    /**
+     * Test read with a timeout of Integer.MAX_VALUE
+     */
+    public void testTimedRead9() throws IOException {
+        withConnection((s1, s2) -> {
+            scheduleWrite(s1.getOutputStream(), 99, 2000);
+            s2.setSoTimeout(Integer.MAX_VALUE);
+            int b = s2.getInputStream().read();
+            assertTrue(b == 99);
+        });
+    }
+
+    /**
+     * Test writing after a timed read. The timed read changes the underlying
+     * socket to non-blocking.
+     */
+    public void testTimedWrite1() throws IOException {
+        withConnection((s1, s2) -> {
+            s1.getOutputStream().write(99);
+            s2.setSoTimeout(3000);
+            int b = s2.getInputStream().read();
+            assertTrue(b == 99);
+
+            // schedule thread to read s1 to EOF
+            scheduleReadToEOF(s1.getInputStream(), 3000);
+
+            // write a lot so that write blocks
+            byte[] data = new byte[128*1024];
+            for (int i = 0; i < 100; i++) {
+                s2.getOutputStream().write(data);
+            }
+        });
+    }
+
+    /**
+     * Test async close of writer (after a timed read). The timed read changes
+     * the underlying socket to non-blocking.
+     */
+    public void testTimedWrite2() throws IOException {
+        withConnection((s1, s2) -> {
+            s1.getOutputStream().write(99);
+            s2.setSoTimeout(3000);
+            int b = s2.getInputStream().read();
+            assertTrue(b == 99);
+
+            // schedule s2 to be be closed
+            scheduleClose(s2, 3000);
+
+            // write a lot so that write blocks
+            byte[] data = new byte[128*1024];
+            try {
+                while (true) {
+                    s2.getOutputStream().write(data);
+                }
+            } catch (SocketException expected) { }
+        });
+    }
+
+    /**
+     * Test timed accept where a connection is established immediately
+     */
+    public void testTimedAccept1() throws IOException {
+        Socket s1 = null;
+        Socket s2 = null;
+        try (ServerSocket ss = new ServerSocket(0)) {
+            s1 = new Socket();
+            s1.connect(ss.getLocalSocketAddress());
+            ss.setSoTimeout(30*1000);
+            s2 = ss.accept();
+        } finally {
+            if (s1 != null) s1.close();
+            if (s2 != null) s2.close();
+        }
+    }
+
+    /**
+     * Test timed accept where a connection is established after a short delay
+     */
+    public void testTimedAccept2() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            ss.setSoTimeout(30*1000);
+            scheduleConnect(ss.getLocalSocketAddress(), 2000);
+            Socket s = ss.accept();
+            s.close();
+        }
+    }
+    
+    /**
+     * Test timed accept where the accept times out
+     */
+    public void testTimedAccept3() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            ss.setSoTimeout(2000);
+            try {
+                Socket s = ss.accept();
+                s.close();
+                assertTrue(false);
+            } catch (SocketTimeoutException expected) { }
+        }
+    }
+
+    /**
+     * Test timed accept where a connection is established immediately after a
+     * previous accept timed out.
+     */
+    public void testTimedAccept4() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            ss.setSoTimeout(2000);
+            try {
+                Socket s = ss.accept();
+                s.close();
+                assertTrue(false);
+            } catch (SocketTimeoutException expected) { }
+            try (Socket s1 = new Socket()) {
+                s1.connect(ss.getLocalSocketAddress());
+                Socket s2 = ss.accept();
+                s2.close();
+            }
+        }
+    }
+
+    /**
+     * Test untimed accept where a connection is established after a previous
+     * accept timed out
+     */
+    public void testTimedAccept5() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            ss.setSoTimeout(2000);
+            try {
+                Socket s = ss.accept();
+                s.close();
+                assertTrue(false);
+            } catch (SocketTimeoutException expected) { }
+            ss.setSoTimeout(0);
+            try (Socket s1 = new Socket()) {
+                s1.connect(ss.getLocalSocketAddress());
+                Socket s2 = ss.accept();
+                s2.close();
+            }
+        }
+    }
+
+    /**
+     * Test untimed accept where a connection is established after a previous
+     * accept timed out and after a short delay
+     */
+    public void testTimedAccept6() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            ss.setSoTimeout(2000);
+            try {
+                Socket s = ss.accept();
+                s.close();
+                assertTrue(false);
+            } catch (SocketTimeoutException expected) { }
+            ss.setSoTimeout(0);
+            scheduleConnect(ss.getLocalSocketAddress(), 2000);
+            Socket s = ss.accept();
+            s.close();
+        }
+    }
+
+    /**
+     * Test async close of a timed accept
+     */
+    public void testTimedAccept7() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            ss.setSoTimeout(30*1000);
+            scheduleClose(ss, 2000);
+            try {
+                ss.accept().close();
+                assertTrue(false);
+            } catch (SocketException expected) { }
+        }
+    }
+
+    /**
+     * Test Socket setSoTimeout with a negative timeout. This case is not currently
+     * specified but the long standing behavior is to throw IllegalArgumentException.
+     */
+    @Test(expectedExceptions = { IllegalArgumentException.class })
+    public void testBadTimeout1() throws IOException {
+        try (Socket s = new Socket()) {
+            s.setSoTimeout(-1);
+        }
+    }
+
+    /**
+     * Test ServerSocket setSoTimeout with a negative timeout. This case is not
+     * currently specified but the long standing behavior is to throw
+     * IllegalArgumentException.
+     */
+    @Test(expectedExceptions = { IllegalArgumentException.class })
+    public void testBadTimeout2() throws IOException {
+        try (ServerSocket ss = new ServerSocket()) {
+            ss.setSoTimeout(-1);
+        }
+    }
+
+    interface ThrowingBiConsumer<T, U> {
+        void accept(T t, U u) throws IOException;
+    }
+
+    /**
+     * Invokes the consumer with a connected pair of sockets
+     */
+    static void withConnection(ThrowingBiConsumer<Socket, Socket> consumer)
+        throws IOException
+    {
+        Socket s1 = null;
+        Socket s2 = null;
+        try (ServerSocket ss = new ServerSocket(0)) {
+            s1 = new Socket();
+            s1.connect(ss.getLocalSocketAddress());
+            s2 = ss.accept();
+            consumer.accept(s1, s2);
+        } finally {
+            if (s1 != null) s1.close();
+            if (s2 != null) s2.close();
+        }
+    }
+
+    /**
+     * Schedule c to be closed after a delay
+     */
+    static void scheduleClose(Closeable c, long delay) {
+        schedule(() -> {
+            try {
+                c.close();
+            } catch (IOException ioe) { }
+        }, delay);
+    }
+
+    /**
+     * Schedule a thread to connect to the given end point after a delay
+     */
+    static void scheduleConnect(SocketAddress remote, long delay) {
+        schedule(() -> {
+            try (Socket s = new Socket()) {
+                s.connect(remote);
+            } catch (IOException ioe) { }
+        }, delay);
+    }
+
+    /**
+     * Schedule a thread to read to EOF after a delay
+     */
+    static void scheduleReadToEOF(InputStream in, long delay) {
+        schedule(() -> {
+            byte[] bytes = new byte[8192];
+            try {
+                while (in.read(bytes) != -1) { }
+            } catch (IOException ioe) { }
+        }, delay);
+    }
+
+    /**
+     * Schedule a thread to write after a delay
+     */
+    static void scheduleWrite(OutputStream out, byte[] data, long delay) {
+        schedule(() -> {
+            try {
+                out.write(data);
+            } catch (IOException ioe) { }
+        }, delay);
+    }
+    static void scheduleWrite(OutputStream out, int b, long delay) {
+        scheduleWrite(out, new byte[] { (byte)b }, delay);
+    }
+
+    static void schedule(Runnable task, long delay) {
+        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+        try {
+            executor.schedule(task, delay, TimeUnit.MILLISECONDS);
+        } finally {
+            executor.shutdown();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/Socket/UdpSocket.java	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,76 @@
+/*
+ * 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
+ * @run main UdpSocket
+ * @run main/othervm -Djdk.net.usePlainSocketImpl UdpSocket
+ * @summary Basic test for a Socket to a UDP socket
+ */
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.util.Arrays;
+
+public class UdpSocket {
+
+    static final String MESSAGE = "hello";
+
+    public static void main(String[] args) throws IOException {
+        try (DatagramChannel dc = DatagramChannel.open()) {
+            var loopback = InetAddress.getLoopbackAddress();
+            dc.bind(new InetSocketAddress(loopback, 0));
+
+            int port = ((InetSocketAddress) dc.getLocalAddress()).getPort();
+            try (Socket s = new Socket(loopback, port, false)) {
+
+                // send datagram with socket output stream
+                byte[] array1 = MESSAGE.getBytes("UTF-8");
+                s.getOutputStream().write(array1);
+
+                // receive the datagram
+                var buf = ByteBuffer.allocate(100);
+                SocketAddress remote = dc.receive(buf);
+                buf.flip();
+                if (buf.remaining() != MESSAGE.length())
+                    throw new RuntimeException("Unexpected size");
+
+                // echo the datagram
+                dc.send(buf, remote);
+
+                // receive datagram with the socket input stream
+                byte[] array2 = new byte[100];
+                int n = s.getInputStream().read(array2);
+                if (n != MESSAGE.length())
+                    throw new RuntimeException("Unexpected size");
+                if (!Arrays.equals(array1, 0, n, array2, 0, n))
+                    throw new RuntimeException("Unexpected contents");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/BadSocketImpls.java	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,147 @@
+/*
+ * 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
+ * @summary Combinations of bad SocketImpls
+ * @run testng/othervm BadSocketImpls
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl BadSocketImpls
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketImpl;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import static java.lang.String.format;
+import static java.lang.System.out;
+import static org.testng.Assert.fail;
+
+public class BadSocketImpls {
+
+    Class<IOException> IOE = IOException.class;
+
+    /**
+     * Tests that a server-side custom socket impl, whose accept is a no-op,
+     * does not have any adverse negative effects. Default accept impl.
+     */
+    @Test
+    public void testNoOpAccept() throws IOException {
+        ServerSocket ss = new ServerSocket(new NoOpSocketImpl()) { };
+        try (ss) {
+            ss.bind(new InetSocketAddress(0));
+            assertThrows(IOE, ss::accept);
+        }
+    }
+
+    /**
+     * Tests that a server-side custom socket impl, whose accept is a no-op,
+     * does not have any adverse negative effects. Custom accept, client has
+     * an explicit null impl.
+     */
+    @Test
+    public void testNoOpAcceptWithNullClientImpl() throws IOException {
+        ServerSocket ss = new ServerSocket(new NoOpSocketImpl()) {
+            @Override
+            public Socket accept() throws IOException {
+                Socket s = new Socket((SocketImpl)null) { };
+                implAccept(s);
+                return s;
+            }
+        };
+        try (ss) {
+            ss.bind(new InetSocketAddress(0));
+            assertThrows(IOE, ss::accept);
+        }
+    }
+
+    /**
+     * Tests that a server-side custom socket impl, whose accept is a no-op,
+     * does not have any adverse negative effects. Custom accept, client has
+     * a platform impl.
+     */
+    @Test
+    public void testNoOpAcceptWithPlatformClientImpl() throws IOException {
+        ServerSocket ss = new ServerSocket(new NoOpSocketImpl()) {
+            @Override
+            public Socket accept() throws IOException {
+                Socket s = new Socket();
+                implAccept(s);
+                return s;
+            }
+        };
+        try (ss) {
+            ss.bind(new InetSocketAddress(0));
+            assertThrows(IOE, ss::accept);
+        }
+    }
+
+    static class NoOpSocketImpl extends SocketImpl {
+        @Override protected void create(boolean b) { }
+        @Override protected void connect(String s, int i) { }
+        @Override protected void connect(InetAddress inetAddress, int i) { }
+        @Override protected void connect(SocketAddress socketAddress, int i) { }
+        @Override protected void bind(InetAddress inetAddress, int i) { }
+        @Override protected void listen(int i) { }
+        @Override protected void accept(SocketImpl socket) { }
+        @Override protected InputStream getInputStream() { return null; }
+        @Override protected OutputStream getOutputStream() { return null; }
+        @Override protected int available() { return 0; }
+        @Override protected void close() { }
+        @Override protected void sendUrgentData(int i) { }
+        @Override public void setOption(int i, Object o) { }
+        @Override public Object getOption(int i) { return null; }
+    }
+
+    static <T extends Throwable> void assertThrows(Class<T> throwableClass,
+                                                   ThrowingRunnable runnable) {
+        try {
+            runnable.run();
+            fail(format("Expected %s to be thrown, but nothing was thrown",
+                        throwableClass.getSimpleName()));
+        } catch (Throwable t) {
+            if (!throwableClass.isInstance(t)) {
+                fail(format("Expected %s to be thrown, but %s was thrown",
+                            throwableClass.getSimpleName(),
+                            t.getClass().getSimpleName()),
+                     t);
+            }
+            out.println("caught expected exception: " + t);
+        }
+    }
+
+    interface ThrowingRunnable {
+        void run() throws Throwable;
+    }
+
+    @BeforeMethod
+    public void breakBetweenTests() {
+        System.out.println("\n-------\n");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/SocketImplCombinations.java	Sat Mar 09 12:54:20 2019 +0000
@@ -0,0 +1,844 @@
+/*
+ * 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
+ * @modules java.base/java.net:+open java.base/sun.nio.ch:+open
+ * @run testng/othervm SocketImplCombinations
+ * @run testng/othervm -Djdk.net.usePlainSocketImpl SocketImplCombinations
+ * @summary Test Socket and ServerSocket with combinations of SocketImpls
+ */
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketImpl;
+import java.net.SocketImplFactory;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.function.BiConsumer;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class SocketImplCombinations {
+
+    /**
+     * Test creating an unconnected Socket, it should be created with a platform SocketImpl.
+     */
+    public void testNewSocket1() throws IOException {
+        try (Socket s = new Socket()) {
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(isSocksSocketImpl(si));
+            SocketImpl delegate = getDelegate(si);
+            assertTrue(isPlatformSocketImpl(delegate));
+        }
+    }
+
+    /**
+     * Test creating a connected Socket, it should be created with a platform SocketImpl.
+     */
+    public void testNewSocket2() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            try (Socket s = new Socket(ss.getInetAddress(), ss.getLocalPort())) {
+                SocketImpl si = getSocketImpl(s);
+                assertTrue(isSocksSocketImpl(si));
+                SocketImpl delegate = getDelegate(si);
+                assertTrue(isPlatformSocketImpl(delegate));
+            }
+        }
+    }
+
+    /**
+     * Test creating a Socket for a DIRECT connection, it should be created with a
+     * platform SocketImpl.
+     */
+    public void testNewSocket3() throws IOException {
+        try (Socket s = new Socket(Proxy.NO_PROXY)) {
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(isPlatformSocketImpl(si));
+        }
+    }
+
+    /**
+     * Test creating a Socket for a SOCKS connection, it should be created with a
+     * SOCKS SocketImpl.
+     */
+    public void testNewSocket4() throws IOException {
+        var address = new InetSocketAddress("127.0.0.1", 1080);
+        var socksProxy = new Proxy(Proxy.Type.SOCKS, address);
+        try (Socket s = new Socket(socksProxy)) {
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(isSocksSocketImpl(si));
+            SocketImpl delegate = getDelegate(si);
+            assertTrue(isPlatformSocketImpl(delegate));
+        }
+    }
+
+    /**
+     * Test creating a Socket for a HTTP proxy connection, it should be created with
+     * a HTTP proxy SocketImpl.
+     */
+    public void testNewSocket5() throws IOException {
+        var address = new InetSocketAddress("127.0.0.1", 8080);
+        var httpProxy = new Proxy(Proxy.Type.HTTP, address);
+        try (Socket s = new Socket(httpProxy)) {
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(isHttpConnectSocketImpl(si));
+            SocketImpl delegate = getDelegate(si);
+            assertTrue(isPlatformSocketImpl(delegate));
+        }
+    }
+
+    /**
+     * Test creating a Socket no SocketImpl. A platform SocketImpl should be
+     * created lazily.
+     */
+    public void testNewSocket6() throws IOException {
+        Socket s = new Socket((SocketImpl) null) { };
+        try (s) {
+            assertTrue(getSocketImpl(s) == null);
+            s.bind(new InetSocketAddress(0));   // force SocketImpl to be created
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(isSocksSocketImpl(si));
+            SocketImpl delegate = getDelegate(si);
+            assertTrue(isPlatformSocketImpl(delegate));
+        }
+    }
+
+    /**
+     * Test creating a Socket with a custom SocketImpl.
+     */
+    public void testNewSocket7() throws IOException {
+        Socket s = new Socket(new CustomSocketImpl(false)) { };
+        try (s) {
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(si instanceof CustomSocketImpl);
+        }
+    }
+
+    /**
+     * Test creating a Socket when there is a SocketImplFactory set.
+     */
+    public void testNewSocket8() throws IOException {
+        setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+        try (Socket s = new Socket()) {
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(si instanceof CustomSocketImpl);
+        } finally {
+            setSocketSocketImplFactory(null);
+        }
+    }
+
+    /**
+     * Test creating a Socket for a DIRECT connection when there is a
+     * SocketImplFactory set.
+     */
+    public void testNewSocket9() throws IOException {
+        setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+        try (Socket s = new Socket(Proxy.NO_PROXY)) {
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(si instanceof CustomSocketImpl);
+        } finally {
+            setSocketSocketImplFactory(null);
+        }
+    }
+
+    /**
+     * Test creating a Socket for a SOCKS connection when there is a
+     * SocketImplFactory set.
+     */
+    public void testNewSocket10() throws IOException {
+        var address = new InetSocketAddress("127.0.0.1", 1080);
+        var socksProxy = new Proxy(Proxy.Type.SOCKS, address);
+        setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+        try (Socket s = new Socket(socksProxy)) {
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(isSocksSocketImpl(si));
+            SocketImpl delegate = getDelegate(si);
+            assertTrue(isPlatformSocketImpl(delegate));
+        } finally {
+            setSocketSocketImplFactory(null);
+        }
+    }
+
+    /**
+     * Test creating a Socket for a HTTP proxy connection when there is a
+     * SocketImplFactory set.
+     */
+    public void testNewSocket11() throws IOException {
+        var address = new InetSocketAddress("127.0.0.1", 8080);
+        var httpProxy = new Proxy(Proxy.Type.HTTP, address);
+        setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+        try (Socket s = new Socket(httpProxy)) {
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(isHttpConnectSocketImpl(si));
+            SocketImpl delegate = getDelegate(si);
+            assertTrue(isPlatformSocketImpl(delegate));
+        } finally {
+            setSocketSocketImplFactory(null);
+        }
+    }
+
+    /**
+     * Test creating a Socket no SocketImpl when there is a SocketImplFactory set.
+     */
+    public void testNewSocket12() throws IOException {
+        setSocketSocketImplFactory(() -> new CustomSocketImpl(false));
+        try {
+            Socket s = new Socket((SocketImpl) null) { };
+            try (s) {
+                assertTrue(getSocketImpl(s) == null);
+                s.bind(new InetSocketAddress(0));   // force SocketImpl to be created
+                assertTrue(getSocketImpl(s) instanceof CustomSocketImpl);
+            }
+        } finally {
+            setSocketSocketImplFactory(null);
+        }
+    }
+
+    /**
+     * Test creating an unbound ServerSocket, it should be created with a platform
+     * SocketImpl.
+     */
+    public void testNewServerSocket1() throws IOException {
+        try (ServerSocket ss = new ServerSocket()) {
+            SocketImpl si = getSocketImpl(ss);
+            assertTrue(isPlatformSocketImpl(si));
+        }
+    }
+
+    /**
+     * Test creating a bound ServerSocket, it should be created with a platform
+     * SocketImpl.
+     */
+    public void testNewServerSocket2() throws IOException {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            SocketImpl si = getSocketImpl(ss);
+            assertTrue(isPlatformSocketImpl(si));
+        }
+    }
+
+    /**
+     * Test creating a ServerSocket with a custom SocketImpl.
+     */
+    public void testNewServerSocket3() throws IOException {
+        ServerSocket ss = new ServerSocket(new CustomSocketImpl(true)) { };
+        try (ss) {
+            SocketImpl si = getSocketImpl(ss);
+            assertTrue(si instanceof CustomSocketImpl);
+        }
+    }
+
+    /**
+     * Test creating an unbound ServerSocket when there is a SocketImplFactory set.
+     */
+    public void testNewServerSocket4() throws IOException {
+        setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+        try (ServerSocket ss = new ServerSocket()) {
+            SocketImpl si = getSocketImpl(ss);
+            assertTrue(si instanceof CustomSocketImpl);
+        } finally {
+            setServerSocketImplFactory(null);
+        }
+    }
+
+    /**
+     * Test creating a bound ServerSocket when there is a SocketImplFactory set.
+     */
+    public void testNewServerSocket5() throws IOException {
+        setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+        try (ServerSocket ss = new ServerSocket(0)) {
+            SocketImpl si = getSocketImpl(ss);
+            assertTrue(si instanceof CustomSocketImpl);
+        } finally {
+            setServerSocketImplFactory(null);
+        }
+    }
+
+    /**
+     * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl,
+     * the Socket to accept is created with no SocketImpl.
+     */
+    public void testServerSocketAccept1() throws IOException {
+        var socket = new Socket((SocketImpl) null) { };
+        assertTrue(getSocketImpl(socket) == null);
+
+        serverSocketAccept(socket, (ss, s) -> {
+            assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+            assertTrue(s == socket);
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(isPlatformSocketImpl(si));
+            checkFields(si);
+        });
+    }
+
+    /**
+     * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl,
+     * the Socket to accept is created with no SocketImpl, and there is a custom
+     * client SocketImplFactory set.
+     */
+    public void testServerSocketAccept2() throws IOException {
+        var socket = new Socket((SocketImpl) null) { };
+        assertTrue(getSocketImpl(socket) == null);
+
+        serverSocketAccept(socket, () -> new CustomSocketImpl(false), (ss, s) -> {
+            assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+            assertTrue(s == socket);
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(si instanceof CustomSocketImpl);
+            checkFields(si);
+        });
+    }
+
+    /**
+     * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl,
+     * the Socket to accept is created with a SocketImpl that delegates to a
+     * platform SocketImpl.
+     */
+    public void testServerSocketAccept3() throws IOException {
+        var socket = new Socket();
+        SocketImpl si = getSocketImpl(socket);
+        assertTrue(isSocksSocketImpl(si));
+        SocketImpl delegate = getDelegate(si);
+        assertTrue(isPlatformSocketImpl(delegate));
+
+        serverSocketAccept(socket, (ss, s) -> {
+            assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+            assertTrue(s == socket);
+            assertTrue(getSocketImpl(s) == si);
+            assertTrue(getDelegate(si) == delegate);
+            checkFields(delegate);
+        });
+    }
+
+    /**
+     * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl,
+     * the Socket to accept is created with a custom SocketImpl.
+     */
+    public void testServerSocketAccept4a() throws IOException {
+        SocketImpl clientImpl = new CustomSocketImpl(false);
+        Socket socket = new Socket(clientImpl) { };
+        assertTrue(getSocketImpl(socket) == clientImpl);
+
+        serverSocketAccept(socket, (ss, s) -> {
+            assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+            assertTrue(s == socket);
+            assertTrue(getSocketImpl(s) == clientImpl);
+            checkFields(clientImpl);
+        });
+    }
+
+    public void testServerSocketAccept4b() throws IOException {
+        SocketImpl clientImpl = new CustomSocketImpl(false);
+        Socket socket = new Socket(clientImpl) { };
+        assertTrue(getSocketImpl(socket) == clientImpl);
+
+        serverSocketAccept(socket, () -> new CustomSocketImpl(false), (ss, s) -> {
+            assertTrue(isPlatformSocketImpl(getSocketImpl(ss)));
+            assertTrue(s == socket);
+            assertTrue(getSocketImpl(s) == clientImpl);
+            checkFields(clientImpl);
+        });
+    }
+
+    /**
+     * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl,
+     * the Socket to accept is created no SocketImpl.
+     */
+    public void testServerSocketAccept5a() throws IOException {
+        SocketImpl serverImpl = new CustomSocketImpl(true);
+        try (ServerSocket ss = new ServerSocket(serverImpl) { }) {
+            ss.bind(new InetSocketAddress(0));
+            expectThrows(IOException.class, ss::accept);
+        }
+    }
+    
+    public void testServerSocketAccept5b() throws IOException {
+        var socket = new Socket((SocketImpl) null) { };
+        assertTrue(getSocketImpl(socket) == null);
+
+        SocketImpl serverImpl = new CustomSocketImpl(true);
+        try (ServerSocket ss = serverSocketToAccept(serverImpl, socket)) {
+            expectThrows(IOException.class, ss::accept);
+        } finally {
+            socket.close();
+        }
+    }
+
+    public void testServerSocketAccept5c() throws IOException {
+        setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+        try (ServerSocket ss = new ServerSocket(0)) {
+            expectThrows(IOException.class, ss::accept);
+        } finally {
+            setServerSocketImplFactory(null);
+        }
+    }
+
+    public void testServerSocketAccept5d() throws IOException {
+        var socket = new Socket((SocketImpl) null) { };
+        assertTrue(getSocketImpl(socket) == null);
+
+        setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+        try (ServerSocket ss = serverSocketToAccept(socket)) {
+            expectThrows(IOException.class, ss::accept);
+        } finally {
+            setServerSocketImplFactory(null);
+            socket.close();
+        }
+    }
+
+    /**
+     * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl,
+     * the Socket to accept is created with no SocketImpl, and there is a custom
+     * client SocketImplFactory set.
+     */
+    public void testServerSocketAccept6() throws Exception {
+        var socket = new Socket((SocketImpl) null) { };
+        assertTrue(getSocketImpl(socket) == null);
+
+        SocketImpl serverImpl = new CustomSocketImpl(true);
+        SocketImplFactory clientFactory = () -> new CustomSocketImpl(false);
+        serverSocketAccept(serverImpl, socket, clientFactory, (ss, s) -> {
+            assertTrue(getSocketImpl(ss) == serverImpl);
+            SocketImpl si = getSocketImpl(s);
+            assertTrue(si instanceof CustomSocketImpl);
+            checkFields(si);
+        });
+    }
+
+    /**
+     * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl,
+     * the Socket to accept is created with a SocketImpl that delegates to a
+     * platform SocketImpl.
+     */
+    public void testServerSocketAccept7a() throws IOException {
+        var socket = new Socket();
+        SocketImpl si = getSocketImpl(socket);
+        assertTrue(isSocksSocketImpl(si));
+        SocketImpl delegate = getDelegate(si);
+        assertTrue(isPlatformSocketImpl(delegate));
+
+        SocketImpl serverImpl = new CustomSocketImpl(true);
+        try (ServerSocket ss = serverSocketToAccept(serverImpl, socket)) {
+            expectThrows(IOException.class, ss::accept);
+        } finally {
+            socket.close();
+        }
+    }
+
+    public void testServerSocketAccept7b() throws IOException {
+        var socket = new Socket();
+        SocketImpl si = getSocketImpl(socket);
+        assertTrue(isSocksSocketImpl(si));
+        SocketImpl delegate = getDelegate(si);
+        assertTrue(isPlatformSocketImpl(delegate));
+
+        setServerSocketImplFactory(() -> new CustomSocketImpl(true));
+        try (ServerSocket ss = serverSocketToAccept(socket)) {
+            expectThrows(IOException.class, ss::accept);
+        } finally {
+            setServerSocketImplFactory(null);
+            socket.close();
+        }
+    }
+
+    /**
+     * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl,
+     * the Socket to accept is created with a custom SocketImpl.
+     */
+    public void testServerSocketAccept8() throws Exception {
+        SocketImpl clientImpl = new CustomSocketImpl(false);
+        Socket socket = new Socket(clientImpl) { };
+        assertTrue(getSocketImpl(socket) == clientImpl);
+
+        SocketImpl serverImpl = new CustomSocketImpl(true);
+        SocketImplFactory clientFactory = () -> new CustomSocketImpl(false);
+        serverSocketAccept(serverImpl, socket, clientFactory, (ss, s) -> {
+            assertTrue(getSocketImpl(ss) == serverImpl);
+            assertTrue(getSocketImpl(s) == clientImpl);
+            checkFields(clientImpl);
+        });
+    }
+
+    /**
+     * Creates a ServerSocket that returns the given Socket from accept.
+     * The consumer is invoked with the server socket and the accepted socket.
+     */
+    static void serverSocketAccept(Socket socket,
+                                   BiConsumer<ServerSocket, Socket> consumer)
+        throws IOException
+    {
+        Socket s1 = null;
+        Socket s2 = null;
+        try (ServerSocket ss = serverSocketToAccept(socket)) {
+            s1 = new Socket(ss.getInetAddress(), ss.getLocalPort());
+            s2 = ss.accept();
+            consumer.accept(ss, s2);
+        } finally {
+            if (s1 != null) s1.close();
+            if (s2 != null) s2.close();
+        }
+    }
+
+    /**
+     * Creates a ServerSocket that returns the given Socket from accept. The
+     * given SocketImplFactory is set during the accept and the consumer is
+     * invoked when the server socket and the accepted socket.
+     */
+    static void serverSocketAccept(Socket socket,
+                                   SocketImplFactory factory,
+                                   BiConsumer<ServerSocket, Socket> consumer)
+        throws IOException
+    {
+        Socket s1 = null;
+        Socket s2 = null;
+        try (ServerSocket ss = serverSocketToAccept(socket)) {
+            s1 = new Socket(ss.getInetAddress(), ss.getLocalPort());
+            setSocketSocketImplFactory(factory);
+            try {
+                s2 = ss.accept();
+            } finally {
+                setSocketSocketImplFactory(null);
+            }
+            consumer.accept(ss, s2);
+        } finally {
+            if (s1 != null) s1.close();
+            if (s2 != null) s2.close();
+        }
+    }
+
+    /**
+     * Creates a ServerSocket with a SocketImpl returns the given Socket from
+     * accept. The given SocketImplFactory is set during the accept and the
+     * consumer is invoked when the server socket and the accepted socket.
+     */
+    static void serverSocketAccept(SocketImpl impl,
+                                   Socket socket,
+                                   SocketImplFactory factory,
+                                   BiConsumer<ServerSocket, Socket> consumer)
+        throws IOException
+    {
+        Socket s1 = null;
+        Socket s2 = null;
+        try (ServerSocket ss = serverSocketToAccept(impl, socket)) {
+            s1 = new Socket(ss.getInetAddress(), ss.getLocalPort());
+            setSocketSocketImplFactory(factory);
+            try {
+                s2 = ss.accept();
+            } finally {
+                setSocketSocketImplFactory(null);
+            }
+            consumer.accept(ss, s2);
+        } finally {
+            if (s1 != null) s1.close();
+            if (s2 != null) s2.close();
+        }
+    }
+
+    /**
+     * Creates a ServerSocket that returns the given Socket from accept.
+     */
+    static ServerSocket serverSocketToAccept(Socket s) throws IOException {
+        return new ServerSocket(0) {
+            @Override
+            public Socket accept() throws IOException {
+                implAccept(s);
+                return s;
+            }
+        };
+    }
+
+    /**
+     * Creates a ServerSocket with a SocketImpl that returns the given Socket
+     * from accept.
+     */
+    static ServerSocket serverSocketToAccept(SocketImpl impl, Socket s) throws IOException {
+        ServerSocket ss = new ServerSocket(impl) {
+            @Override
+            public Socket accept() throws IOException {
+                implAccept(s);
+                return s;
+            }
+        };
+        ss.bind(new InetSocketAddress(0));
+        return ss;
+    }
+
+    /**
+     * Returns the socket's SocketImpl
+     */
+    static SocketImpl getSocketImpl(Socket s) {
+        try {
+            Field f = Socket.class.getDeclaredField("impl");
+            f.setAccessible(true);
+            return (SocketImpl) f.get(s);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns the server socket's SocketImpl
+     */
+    static SocketImpl getSocketImpl(ServerSocket ss) {
+        try {
+            Field f = ServerSocket.class.getDeclaredField("impl");
+            f.setAccessible(true);
+            return (SocketImpl) f.get(ss);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns the SocketImpl that the given SocketImpl delegates to
+     */
+    static SocketImpl getDelegate(SocketImpl si) {
+        try {
+            Class<?> clazz = Class.forName("java.net.DelegatingSocketImpl");
+            Field f = clazz.getDeclaredField("delegate");
+            f.setAccessible(true);
+            return (SocketImpl) f.get(si);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns the value of a SocketImpl field
+     */
+    static <T> T get(SocketImpl si, String name) {
+        try {
+            Field f = SocketImpl.class.getDeclaredField(name);
+            f.setAccessible(true);
+            return (T) f.get(si);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Sets the value of SocketImpl field
+     */
+    static void set(SocketImpl si, String name, Object value) {
+        try {
+            Field f = SocketImpl.class.getDeclaredField(name);
+            f.setAccessible(true);
+            f.set(si, value);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns true if the SocketImpl is a PlatformSocketImpl
+     */
+    static boolean isPlatformSocketImpl(SocketImpl si) {
+        try {
+            Class<?> clazz = Class.forName("sun.net.PlatformSocketImpl");
+            return clazz.isInstance(si);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns true if the SocketImpl is a SocksSocketImpl
+     */
+    static boolean isSocksSocketImpl(SocketImpl si) {
+        try {
+            Class<?> clazz = Class.forName("java.net.SocksSocketImpl");
+            return clazz.isInstance(si);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns true if the SocketImpl is a HttpConnectSocketImpl
+     */
+    static boolean isHttpConnectSocketImpl(SocketImpl si) {
+        try {
+            Class<?> clazz = Class.forName("java.net.HttpConnectSocketImpl");
+            return clazz.isInstance(si);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Socket.setSocketImplFactory(SocketImplFactory)
+     */
+    static void setSocketSocketImplFactory(SocketImplFactory factory) {
+        try {
+            Field f = Socket.class.getDeclaredField("factory");
+            f.setAccessible(true);
+            f.set(null, factory);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * ServerSocket.setSocketFactory(SocketImplFactory)
+     */
+    static void setServerSocketImplFactory(SocketImplFactory factory) {
+        try {
+            Field f = ServerSocket.class.getDeclaredField("factory");
+            f.setAccessible(true);
+            f.set(null, factory);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Checks the 4 protected fields of a SocketImpl to make sure that they
+     * have been initialized.
+     */
+    static void checkFields(SocketImpl si) {
+        FileDescriptor fd = get(si, "fd");
+        InetAddress address = get(si, "address");
+        int port = get(si, "port");
+        int localport = get(si, "localport");
+        assertTrue(fd.valid() && address != null && port != 0 && localport != 0);
+    }
+
+    /**
+     * Custom SocketImpl that is layed on a SocketChannel or ServerSocketChannel
+     */
+    static class CustomSocketImpl extends SocketImpl {
+        private final boolean server;
+        private ServerSocketChannel ssc;
+        private SocketChannel sc;
+
+        CustomSocketImpl(boolean server) {
+            this.server = server;
+        }
+
+        @Override
+        protected void create(boolean stream) throws IOException {
+            if (server) {
+                ssc = ServerSocketChannel.open();
+            } else {
+                sc = SocketChannel.open();
+            }
+        }
+
+        @Override
+        protected void connect(String host, int port) throws IOException {
+            connect(new InetSocketAddress(host, port), 0);
+        }
+
+        @Override
+        protected void connect(InetAddress address, int port) throws IOException {
+            connect(new InetSocketAddress(address, port), 0);
+        }
+
+        @Override
+        protected void connect(SocketAddress remote, int timeout) throws IOException {
+            sc.connect(remote);
+            super.address = ((InetSocketAddress) remote).getAddress();
+            super.port = ((InetSocketAddress) remote).getPort();
+        }
+
+        @Override
+        protected void bind(InetAddress address, int port) throws IOException {
+            if (server) {
+                ssc.bind(new InetSocketAddress(address, port));
+                super.localport = ssc.socket().getLocalPort();
+            } else {
+                sc.bind(new InetSocketAddress(address, port));
+                super.localport = sc.socket().getLocalPort();
+            }
+            super.address = address;
+        }
+
+        @Override
+        protected void listen(int backlog) {
+            // do nothing
+        }
+
+        @Override
+        protected void accept(SocketImpl si) throws IOException {
+            SocketChannel peer = ssc.accept();
+            FileDescriptor fd;
+            try {
+                Class<?> clazz = Class.forName("sun.nio.ch.SocketChannelImpl");
+                Field f = clazz.getDeclaredField("fd");
+                f.setAccessible(true);
+                fd = (FileDescriptor) f.get(peer);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+            set(si, "fd", fd);
+            set(si, "address", peer.socket().getInetAddress());
+            set(si, "port", peer.socket().getPort());
+            set(si, "localport", peer.socket().getLocalPort());
+        }
+
+        @Override
+        protected InputStream getInputStream() {
+            throw new RuntimeException();
+        }
+
+        @Override
+        protected OutputStream getOutputStream() {
+            throw new RuntimeException();
+        }
+
+        @Override
+        protected int available() {
+            return 0;
+        }
+
+        @Override
+        protected void close() {
+        }
+
+        @Override
+        protected void sendUrgentData(int data) {
+            throw new RuntimeException();
+        }
+
+        @Override
+        public void setOption(int option, Object value) {
+            throw new RuntimeException();
+        }
+
+        @Override
+        public Object getOption(int option) {
+            throw new RuntimeException();
+        }
+    }
+}
--- a/test/jdk/jdk/jfr/event/io/TestInstrumentation.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/test/jdk/jdk/jfr/event/io/TestInstrumentation.java	Sat Mar 09 12:54:20 2019 +0000
@@ -105,14 +105,14 @@
         "java/io/FileOutputStream::write::(I)V",
         "java/io/FileOutputStream::write::([B)V",
         "java/io/FileOutputStream::write::([BII)V",
-        "java/net/SocketInputStream::read::()I",
-        "java/net/SocketInputStream::read::([B)I",
-        "java/net/SocketInputStream::read::([BII)I",
-        "java/net/SocketInputStream::close::()V",
-        "java/net/SocketOutputStream::write::(I)V",
-        "java/net/SocketOutputStream::write::([B)V",
-        "java/net/SocketOutputStream::write::([BII)V",
-        "java/net/SocketOutputStream::close::()V",
+        "java/net/Socket$SocketInputStream::read::()I",
+        "java/net/Socket$SocketInputStream::read::([B)I",
+        "java/net/Socket$SocketInputStream::read::([BII)I",
+        "java/net/Socket$SocketInputStream::close::()V",
+        "java/net/Socket$SocketOutputStream::write::(I)V",
+        "java/net/Socket$SocketOutputStream::write::([B)V",
+        "java/net/Socket$SocketOutputStream::write::([BII)V",
+        "java/net/Socket$SocketOutputStream::close::()V",
         "java/nio/channels/FileChannel::read::([Ljava/nio/ByteBuffer;)J",
         "java/nio/channels/FileChannel::write::([Ljava/nio/ByteBuffer;)J",
         "java/nio/channels/SocketChannel::open::()Ljava/nio/channels/SocketChannel;",
--- a/test/micro/org/openjdk/bench/java/net/SocketReadWrite.java	Fri Mar 08 17:45:40 2019 -0500
+++ b/test/micro/org/openjdk/bench/java/net/SocketReadWrite.java	Sat Mar 09 12:54:20 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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
@@ -22,86 +22,206 @@
  */
 package org.openjdk.bench.java.net;
 
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import org.openjdk.jmh.annotations.BenchmarkMode;
-import org.openjdk.jmh.annotations.Benchmark;
-import org.openjdk.jmh.annotations.Mode;
-import org.openjdk.jmh.annotations.OutputTimeUnit;
-import org.openjdk.jmh.annotations.Scope;
-import org.openjdk.jmh.annotations.Setup;
-import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.TearDown;
+/**
+ * Tests socket read/write.
+ */
 
-/**
- * Tests the overheads of I/O API.
- * This test is known to depend heavily on network conditions and paltform.
- */
 @BenchmarkMode(Mode.Throughput)
-@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@OutputTimeUnit(TimeUnit.SECONDS)
 @State(Scope.Thread)
 public class SocketReadWrite {
 
-    private OutputStream os;
-    private InputStream is;
-    private ServerSocket ss;
-    private Socket s1, s2;
-    private ReadThread rt;
+    static final InetAddress address = InetAddress.getLoopbackAddress();
+    public static final int TIMEOUT = 10000;
+
+    static class EchoServer implements Runnable {
+        final ServerSocket ss;
+        final int port;
+        final CountDownLatch startedLatch;
+        final int size;
+        final boolean timeout;
+        List<ServerThread> threads = new ArrayList<>();
+        volatile boolean isDone = false;
+
+        public EchoServer(CountDownLatch await, int size, boolean timeout) throws IOException {
+            this.size = size;
+            this.timeout = timeout;
+            ss = new ServerSocket(0);
+            port = ss.getLocalPort();
+            this.startedLatch = await;
+        }
+
+        @Override
+        public void run() {
+            startedLatch.countDown();
+            while (!isDone) {
+                try {
+                    Socket s = ss.accept();
+                    s.setTcpNoDelay(true);
+                    if (timeout) {
+                        s.setSoTimeout(TIMEOUT);
+                    }
+                    ServerThread st = new ServerThread(s, size);
+                    threads.add(st);
+                    new Thread(st).start();
+                } catch (IOException e) {
+                    if (!isDone) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+
+        synchronized void close() throws IOException {
+            if (!isDone) {
+                isDone = true;
+                ss.close();
+                for (ServerThread st : threads) {
+                    st.close();
+                }
+            }
+        }
+
+        static EchoServer instance = null;
+
+        static synchronized EchoServer startServer(int size, boolean timeout) throws IOException {
+            if (instance == null) {
+                CountDownLatch started = new CountDownLatch(1);
+                EchoServer s = new EchoServer(started, size, timeout);
+                new Thread(s).start();
+                try {
+                    started.await(); // wait until server thread started
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                instance = s;
+            }
+            return instance;
+        }
+
+        static class ServerThread implements Runnable {
+
+            final Socket s;
+            final InputStream in;
+            final OutputStream out;
+            final int size;
+            volatile boolean isDone = false;
+
+            ServerThread(Socket s, int size) throws IOException {
+                this.s = s;
+                this.size = size;
+                in = s.getInputStream();
+                out = s.getOutputStream();
+            }
+
+            @Override
+            public void run() {
+                if (size == 1) {
+                    while (!isDone) {
+                        try {
+                            int b = this.in.read();
+                            out.write(b);
+                        } catch (IOException e) {
+                            if (!isDone) {
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+                } else {
+                    byte[] a = new byte[size];
+                    while (!isDone) {
+                        try {
+                            readN(a, size, this.in);
+                            out.write(a);
+                        } catch (IOException e) {
+                            if (!isDone) {
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+                }
+            }
+
+            public void close() throws IOException {
+                isDone = true;
+                s.close();
+            }
+
+        }
+    }
+
+    static void readN(byte[] array, int size, InputStream in) throws IOException {
+        int nread = 0;
+        while (size > 0) {
+            int n = in.read(array, nread, size);
+            if (n < 0) throw new RuntimeException();
+            nread += n;
+            size -= n;
+        }
+    }
+
+    EchoServer server;
+
+    @Param({"1", "1024", "8192", "64000", "128000"})
+    public int size;
+
+    @Param({"false", "true"})
+    public boolean timeout;
+
+    Socket s;
+    InputStream in;
+    OutputStream out;
+    byte[] array;
 
     @Setup
-    public void beforeRun() throws IOException {
-        InetAddress iaddr = InetAddress.getLocalHost();
-
-        ss = new ServerSocket(0);
-        s1 = new Socket(iaddr, ss.getLocalPort());
-        s2 = ss.accept();
-
-        os = s1.getOutputStream();
-        is = s2.getInputStream();
-
-        rt = new ReadThread(is);
-        rt.start();
+    public void setup() throws IOException {
+        server = EchoServer.startServer(size, timeout);
+        int port = server.port;
+        s = new Socket(address, port);
+        s.setTcpNoDelay(true);
+        if (timeout) {
+            s.setSoTimeout(TIMEOUT);
+        }
+        in = s.getInputStream();
+        out = s.getOutputStream();
+        array = new byte[size];
     }
 
     @TearDown
-    public void afterRun() throws IOException, InterruptedException {
-        os.write(0);
-        os.close();
-        is.close();
-        s1.close();
-        s2.close();
-        ss.close();
-        rt.join();
+    public void tearDown() throws IOException {
+        server.close();
+        s.close();
     }
 
     @Benchmark
-    public void test() throws IOException {
-        os.write((byte) 4711);
-    }
-
-    static class ReadThread extends Thread {
-        private InputStream is;
-
-        public ReadThread(InputStream is) {
-            this.is = is;
+    public void echo() throws IOException {
+        if (size == 1) {
+            out.write((byte) 47);
+            int c = in.read();
+        } else {
+            out.write(array);
+            readN(array, size, in);
         }
 
-        public void run() {
-            try {
-                while (is.read() > 0);
-            } catch (SocketException ex) {
-                // ignore - most likely "socket closed", which means shutdown
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
     }
-
 }