8145635: Add TCP_QUICKACK socket option

Reviewed-by: chegar, rriggs, clanger
This commit is contained in:
Vyom Tewari 2017-11-13 14:03:36 +05:30
parent 43006bf8f9
commit 2245db7522
9 changed files with 394 additions and 14 deletions

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2017, 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
@ -35,7 +35,7 @@ ifeq ($(OPENJDK_TARGET_OS), solaris)
SRC := $(TOPDIR)/src/jdk.net/solaris/native/libextnet, \
OPTIMIZATION := LOW, \
CFLAGS := $(CFLAGS_JDKLIB) -I$(SUPPORT_OUTPUTDIR)/headers/jdk.net, \
MAPFILE := $(TOPDIR)/make/mapfiles/libextnet/mapfile-vers, \
MAPFILE := $(TOPDIR)/make/mapfiles/libextnet/mapfile-solaris, \
LDFLAGS := $(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
LIBS := -lsocket -lc -ljava, \
@ -48,4 +48,25 @@ ifeq ($(OPENJDK_TARGET_OS), solaris)
endif
ifeq ($(OPENJDK_TARGET_OS), linux)
$(eval $(call SetupNativeCompilation, BUILD_LIBEXTNET, \
LIBRARY := extnet, \
OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \
SRC := $(TOPDIR)/src/jdk.net/linux/native/libextnet, \
OPTIMIZATION := LOW, \
CFLAGS := $(CFLAGS_JDKLIB) -I$(SUPPORT_OUTPUTDIR)/headers/jdk.net, \
MAPFILE := $(TOPDIR)/make/mapfiles/libextnet/mapfile-linux, \
LDFLAGS := $(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
LIBS := -ljvm -ljava -lc, \
OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libextnet, \
))
$(BUILD_LIBEXTNET): $(call FindLib, java.base, java)
TARGETS += $(BUILD_LIBEXTNET)
endif
################################################################################

View File

@ -0,0 +1,33 @@
#
# Copyright (c) 2017, 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.
#
SUNWprivate_1.1 {
global:
Java_jdk_net_LinuxSocketOptions_setQuickAck0;
Java_jdk_net_LinuxSocketOptions_getQuickAck0;
Java_jdk_net_LinuxSocketOptions_quickAckSupported0;
local:
*;
};

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2017, 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
@ -85,10 +85,18 @@ class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
protected Set<SocketOption<?>> supportedOptions() {
HashSet<SocketOption<?>> options = new HashSet<>(super.supportedOptions());
options.addAll(extendedOptions.options());
addExtSocketOptions(extendedOptions.options(), options);
return options;
}
private void addExtSocketOptions(Set<SocketOption<?>> extOptions,
Set<SocketOption<?>> options) {
// TCP_QUICKACK is applicable for TCP Sockets only.
extOptions.stream()
.filter((option) -> !option.name().equals("TCP_QUICKACK"))
.forEach((option) -> options.add(option));
}
protected void socketSetOption(int opt, Object val) throws SocketException {
if (opt == SocketOptions.SO_REUSEPORT &&
!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2017, 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 jdk.net;
import java.net.SocketException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import jdk.net.ExtendedSocketOptions.PlatformSocketOptions;
class LinuxSocketOptions extends PlatformSocketOptions {
public LinuxSocketOptions() {
}
@Override
void setQuickAck(int fd, boolean on) throws SocketException {
setQuickAck0(fd, on);
}
@Override
boolean getQuickAck(int fd) throws SocketException {
return getQuickAck0(fd);
}
@Override
public boolean quickAckSupported() {
return quickAckSupported0();
}
native static private void setQuickAck0(int fd, boolean on) throws SocketException;
native static private boolean getQuickAck0(int fd) throws SocketException;
native static private boolean quickAckSupported0();
static {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
System.loadLibrary("extnet");
return null;
});
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2017, 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/socket.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <jni.h>
#include <netinet/tcp.h>
#include "jni_util.h"
/*
* Class: jdk_net_LinuxSocketOptions
* Method: setQuickAck
* Signature: (II)V
*/
JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setQuickAck0
(JNIEnv *env, jobject unused, jint fd, jboolean on) {
int optval;
int rv;
optval = (on ? 1 : 0);
rv = setsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &optval, sizeof (optval));
if (rv < 0) {
if (errno == ENOPROTOOPT) {
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
"unsupported socket option");
} else {
JNU_ThrowByNameWithLastError(env, "java/net/SocketException",
"set option TCP_QUICKACK failed");
}
}
}
/*
* Class: jdk_net_LinuxSocketOptions
* Method: getQuickAck
* Signature: (I)Z;
*/
JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_getQuickAck0
(JNIEnv *env, jobject unused, jint fd) {
int on;
socklen_t sz = sizeof (on);
int rv = getsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &on, &sz);
if (rv < 0) {
if (errno == ENOPROTOOPT) {
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
"unsupported socket option");
} else {
JNU_ThrowByNameWithLastError(env, "java/net/SocketException",
"get option TCP_QUICKACK failed");
}
}
return on != 0;
}
/*
* Class: jdk_net_LinuxSocketOptions
* Method: quickAckSupported
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_quickAckSupported0
(JNIEnv *env, jobject unused) {
int one = 1;
int rv, s;
s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
return JNI_FALSE;
}
rv = setsockopt(s, SOL_SOCKET, TCP_QUICKACK, (void *) &one, sizeof (one));
if (rv != 0 && errno == ENOPROTOOPT) {
rv = JNI_FALSE;
} else {
rv = JNI_TRUE;
}
close(s);
return rv;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2017, 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
@ -67,20 +67,53 @@ public final class ExtendedSocketOptions {
public static final SocketOption<SocketFlow> SO_FLOW_SLA = new
ExtSocketOption<SocketFlow>("SO_FLOW_SLA", SocketFlow.class);
/**
* Disable Delayed Acknowledgements.
*
* <p>
* This socket option can be used to reduce or disable delayed
* acknowledgments (ACKs). When {@code TCP_QUICKACK} is enabled, ACKs are
* sent immediately, rather than delayed if needed in accordance to normal
* TCP operation. This option is not permanent, it only enables a switch to
* or from {@code TCP_QUICKACK} mode. Subsequent operations of the TCP
* protocol will once again disable/enable {@code TCP_QUICKACK} mode
* depending on internal protocol processing and factors such as delayed ACK
* timeouts occurring and data transfer, therefore this option needs to be
* set with {@code setOption} after each operation of TCP on a given socket.
*
* <p>
* The value of this socket option is a {@code Boolean} that represents
* whether the option is enabled or disabled. The socket option is specific
* to stream-oriented sockets using the TCP/IP protocol. The exact semantics
* of this socket option are socket type and system dependent.
*
* @since 10
*/
public static final SocketOption<Boolean> TCP_QUICKACK =
new ExtSocketOption<Boolean>("TCP_QUICKACK", Boolean.class);
private static final PlatformSocketOptions platformSocketOptions =
PlatformSocketOptions.get();
private static final boolean flowSupported =
platformSocketOptions.flowSupported();
private static final boolean quickAckSupported =
platformSocketOptions.quickAckSupported();
private static final Set<SocketOption<?>> extendedOptions = options();
static Set<SocketOption<?>> options() {
if (flowSupported)
return Set.of(SO_FLOW_SLA);
else
if (flowSupported) {
if (quickAckSupported) {
return Set.of(SO_FLOW_SLA, TCP_QUICKACK);
} else {
return Set.of(SO_FLOW_SLA);
}
} else if (quickAckSupported) {
return Set.of(TCP_QUICKACK);
} else {
return Collections.<SocketOption<?>>emptySet();
}
}
static {
@ -105,6 +138,8 @@ public final class ExtendedSocketOptions {
assert flowSupported;
SocketFlow flow = checkValueType(value, option.type());
setFlowOption(fd, flow);
} else if (option == TCP_QUICKACK) {
setQuickAckOption(fd, (boolean) value);
} else {
throw new InternalError("Unexpected option " + option);
}
@ -127,6 +162,8 @@ public final class ExtendedSocketOptions {
SocketFlow flow = SocketFlow.create();
getFlowOption(fd, flow);
return flow;
} else if (option == TCP_QUICKACK) {
return getQuickAckOption(fd);
} else {
throw new InternalError("Unexpected option " + option);
}
@ -156,12 +193,21 @@ public final class ExtendedSocketOptions {
}
private static void getFlowOption(FileDescriptor fd, SocketFlow f)
throws SocketException
{
throws SocketException {
int status = platformSocketOptions.getFlowOption(fdAccess.get(fd), f);
f.status(status); // augment the given flow with the status
}
private static void setQuickAckOption(FileDescriptor fd, boolean enable)
throws SocketException {
platformSocketOptions.setQuickAck(fdAccess.get(fd), enable);
}
private static Object getQuickAckOption(FileDescriptor fd)
throws SocketException {
return platformSocketOptions.getQuickAck(fdAccess.get(fd));
}
static class PlatformSocketOptions {
protected PlatformSocketOptions() {}
@ -184,9 +230,13 @@ public final class ExtendedSocketOptions {
return System.getProperty("os.name");
}
});
if ("SunOS".equals(osname))
if ("SunOS".equals(osname)) {
return newInstance("jdk.net.SolarisSocketOptions");
return new PlatformSocketOptions();
} else if ("Linux".equals(osname)) {
return newInstance("jdk.net.LinuxSocketOptions");
} else {
return new PlatformSocketOptions();
}
}
private static final PlatformSocketOptions instance = create();
@ -208,5 +258,17 @@ public final class ExtendedSocketOptions {
boolean flowSupported() {
return false;
}
void setQuickAck(int fd, boolean on) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_QUICKACK option");
}
boolean getQuickAck(int fd) throws SocketException {
throw new UnsupportedOperationException("unsupported TCP_QUICKACK option");
}
boolean quickAckSupported() {
return false;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2017, 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
@ -279,6 +279,9 @@ public class Sockets {
if (flowsupported) {
set.add(ExtendedSocketOptions.SO_FLOW_SLA);
}
if (QuickAck.available) {
set.add(ExtendedSocketOptions.TCP_QUICKACK);
}
set = Collections.unmodifiableSet(set);
options.put(Socket.class, set);
@ -290,6 +293,9 @@ public class Sockets {
if (reuseportsupported) {
set.add(StandardSocketOptions.SO_REUSEPORT);
}
if (QuickAck.available) {
set.add(ExtendedSocketOptions.TCP_QUICKACK);
}
set.add(StandardSocketOptions.IP_TOS);
set = Collections.unmodifiableSet(set);
options.put(ServerSocket.class, set);
@ -331,4 +337,17 @@ public class Sockets {
return Collections.unmodifiableMap(options);
}
/**
* Tells whether TCP_QUICKACK is supported.
*/
static class QuickAck {
static final boolean available;
static {
Set<SocketOption<?>> s = new Socket().supportedOptions();
available = s.contains(ExtendedSocketOptions.TCP_QUICKACK);
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8145635
* @summary Add TCP_QUICKACK socket option
* @modules jdk.net
* @run main QuickAckTest
*/
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.MulticastSocket;
import java.net.ServerSocket;
import java.net.Socket;
import jdk.net.ExtendedSocketOptions;
import jdk.net.Sockets;
public class QuickAckTest {
private static final String LOCAL_HOST = "127.0.0.1";
public static void main(String args[]) throws IOException {
try (ServerSocket ss = new ServerSocket(0);
Socket s = new Socket(LOCAL_HOST, ss.getLocalPort());
DatagramSocket ds = new DatagramSocket(0);
MulticastSocket mc = new MulticastSocket(0)) {
if (ss.supportedOptions().contains(ExtendedSocketOptions.TCP_QUICKACK)) {
Sockets.setOption(ss, ExtendedSocketOptions.TCP_QUICKACK, true);
if (!ss.getOption(ExtendedSocketOptions.TCP_QUICKACK)) {
throw new RuntimeException("Test failed, TCP_QUICKACK should"
+ " have been set");
}
}
if (s.supportedOptions().contains(ExtendedSocketOptions.TCP_QUICKACK)) {
Sockets.setOption(s, ExtendedSocketOptions.TCP_QUICKACK, true);
if (!s.getOption(ExtendedSocketOptions.TCP_QUICKACK)) {
throw new RuntimeException("Test failed, TCP_QUICKACK should"
+ " have been set");
}
}
if (ds.supportedOptions().contains(ExtendedSocketOptions.TCP_QUICKACK)) {
throw new RuntimeException("Test failed, TCP_QUICKACK is applicable"
+ " for TCP Sockets only.");
}
if (mc.supportedOptions().contains(ExtendedSocketOptions.TCP_QUICKACK)) {
throw new RuntimeException("Test failed, TCP_QUICKACK is applicable"
+ " for TCP Sockets only");
}
}
}
}