changeset 60496:035cdb28aa4c

8250521: Configure initial RTO to use minimal retry for loopback connections on Windows Reviewed-by: alanb Contributed-by: nikola.grcevski@microsoft.com
author alanb
date Mon, 10 Aug 2020 12:57:38 +0100
parents e9a5b5fcffdb
children a6bd3cd0c3a2
files src/java.base/windows/native/libnet/net_util_md.c src/java.base/windows/native/libnet/net_util_md.h src/java.base/windows/native/libnio/ch/Net.c
diffstat 3 files changed, 87 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/windows/native/libnet/net_util_md.c	Mon Aug 10 12:12:40 2020 +0300
+++ b/src/java.base/windows/native/libnet/net_util_md.c	Mon Aug 10 12:57:38 2020 +0100
@@ -770,6 +770,57 @@
     return result == SOCKET_ERROR ? WSAGetLastError() : 0;
 }
 
+int
+IsWindows10RS3OrGreater() {
+    OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
+    DWORDLONG const cond_mask = VerSetConditionMask(
+        VerSetConditionMask(
+          VerSetConditionMask(
+            0, VER_MAJORVERSION, VER_GREATER_EQUAL),
+               VER_MINORVERSION, VER_GREATER_EQUAL),
+               VER_BUILDNUMBER,  VER_GREATER_EQUAL);
+
+    osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_WIN10);
+    osvi.dwMinorVersion = LOBYTE(_WIN32_WINNT_WIN10);
+    osvi.dwBuildNumber  = 16299; // RS3 (Redstone 3)
+
+    return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, cond_mask) != 0;
+}
+
+/**
+ * Shortens the default Windows socket
+ * connect timeout. Recommended for usage
+ * on the loopback adapter only.
+ */
+JNIEXPORT jint JNICALL
+NET_EnableFastTcpLoopbackConnect(int fd) {
+    TCP_INITIAL_RTO_PARAMETERS rto = {
+        TCP_INITIAL_RTO_UNSPECIFIED_RTT,    // Use the default or overriden by the Administrator
+        1                                   // Minimum possible value before Windows 10 RS3
+    };
+
+    /**
+     * In Windows 10 RS3+ we can use the no retransmissions flag to
+     * completely remove the timeout delay, which is fixed to 500ms
+     * if Windows receives RST when the destination port is not open.
+     */
+    if (IsWindows10RS3OrGreater()) {
+        rto.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
+    }
+
+    DWORD result_byte_count = -1;
+    int result = WSAIoctl(fd,                       // descriptor identifying a socket
+                          SIO_TCP_INITIAL_RTO,      // dwIoControlCode
+                          &rto,                     // pointer to TCP_INITIAL_RTO_PARAMETERS structure
+                          sizeof(rto),              // size, in bytes, of the input buffer
+                          NULL,                     // pointer to output buffer
+                          0,                        // size of output buffer
+                          &result_byte_count,       // number of bytes returned
+                          NULL,                     // OVERLAPPED structure
+                          NULL);                    // completion routine
+    return (result == SOCKET_ERROR) ? WSAGetLastError() : 0;
+}
+
 /**
  * See net_util.h for documentation
  */
--- a/src/java.base/windows/native/libnet/net_util_md.h	Mon Aug 10 12:12:40 2020 +0300
+++ b/src/java.base/windows/native/libnet/net_util_md.h	Mon Aug 10 12:57:38 2020 +0100
@@ -26,6 +26,7 @@
 #include <WS2tcpip.h>
 #include <iphlpapi.h>
 #include <icmpapi.h>
+#include <mstcpip.h>
 
 /* used to disable connection reset messages on Windows XP */
 #ifndef SIO_UDP_CONNRESET
@@ -86,10 +87,29 @@
 
 #define GET_PORT(X) ((X)->sa.sa_family == AF_INET ? (X)->sa4.sin_port : (X)->sa6.sin6_port)
 
+/**
+ * With dual socket implementation the
+ * IPv4 addresseses might be mapped as IPv6.
+ * The IPv4 loopback adapter address will
+ * be mapped as the following IPv6 ::ffff:127.0.0.1.
+ * For example, this is done by NET_InetAddressToSockaddr.
+ */
+#define IN6_IS_ADDR_V4MAPPED_LOOPBACK(x) ( \
+    (((x)->s6_words[0] == 0)      &&  \
+     ((x)->s6_words[1] == 0)      &&  \
+     ((x)->s6_words[2] == 0)      &&  \
+     ((x)->s6_words[3] == 0)      &&  \
+     ((x)->s6_words[4] == 0)      &&  \
+     ((x)->s6_words[5] == 0xFFFF) &&  \
+     ((x)->s6_words[6] == 0x007F) &&  \
+     ((x)->s6_words[7] == 0x0100))    \
+)
+
 #define IS_LOOPBACK_ADDRESS(x) ( \
     ((x)->sa.sa_family == AF_INET) ? \
         (ntohl((x)->sa4.sin_addr.s_addr) == INADDR_LOOPBACK) : \
-        (IN6ADDR_ISLOOPBACK(x)) \
+        ((IN6_IS_ADDR_LOOPBACK(&(x)->sa6.sin6_addr)) || \
+         (IN6_IS_ADDR_V4MAPPED_LOOPBACK(&(x)->sa6.sin6_addr))) \
 )
 
 JNIEXPORT int JNICALL NET_SocketClose(int fd);
@@ -119,6 +139,8 @@
 JNIEXPORT int JNICALL NET_WinBind(int s, SOCKETADDRESS *sa, int len,
                                   jboolean exclBind);
 
+JNIEXPORT jint JNICALL NET_EnableFastTcpLoopbackConnect(int fd);
+
 /* XP versions of the native routines */
 
 JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0_XP
--- a/src/java.base/windows/native/libnio/ch/Net.c	Mon Aug 10 12:12:40 2020 +0300
+++ b/src/java.base/windows/native/libnio/ch/Net.c	Mon Aug 10 12:57:38 2020 +0100
@@ -226,13 +226,25 @@
 {
     SOCKETADDRESS sa;
     int rv;
+    int so_rv;
     int sa_len = 0;
     SOCKET s = (SOCKET)fdval(env, fdo);
+    int type = 0, optlen = sizeof(type);
 
     if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, preferIPv6) != 0) {
         return IOS_THROWN;
     }
 
+    so_rv = getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&type, &optlen);
+
+    /**
+     * Windows has a very long socket connect timeout of 2 seconds.
+     * If it's the loopback adapter we can shorten the wait interval.
+     */
+    if (so_rv == 0 && type == SOCK_STREAM && IS_LOOPBACK_ADDRESS(&sa)) {
+        NET_EnableFastTcpLoopbackConnect((jint)s);
+    }
+
     rv = connect(s, &sa.sa, sa_len);
     if (rv != 0) {
         int err = WSAGetLastError();
@@ -243,9 +255,7 @@
         return IOS_THROWN;
     } else {
         /* Enable WSAECONNRESET errors when a UDP socket is connected */
-        int type = 0, optlen = sizeof(type);
-        rv = getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&type, &optlen);
-        if (rv == 0 && type == SOCK_DGRAM) {
+        if (so_rv == 0 && type == SOCK_DGRAM) {
             setConnectionReset(s, TRUE);
         }
     }