--- old/make/gensrc/GensrcMisc.gmk 2018-06-22 12:23:35.287672510 -0700 +++ new/make/gensrc/GensrcMisc.gmk 2018-06-22 12:23:35.061672516 -0700 @@ -81,6 +81,17 @@ GENSRC_JAVA_BASE += $(GENSRC_SOR_FILE) +ifeq ($(OPENJDK_TARGET_OS), linux) + GENSRC_RSOR_FILE += $(SUPPORT_OUTPUTDIR)/gensrc/jdk.net/rdma/ch/RdmaSocketOptionRegistry.java + + $(GENSRC_RSOR_FILE): \ + $(TOPDIR)/src/jdk.net/share/classes/rdma/ch/RdmaSocketOptionRegistry.java.template + $(generate-preproc-src) + + GENSRC_JAVA_BASE += $(GENSRC_RSOR_FILE) + +endif + ################################################################################ ifneq ($(OPENJDK_TARGET_OS), windows) --- old/make/lib/Lib-java.base.gmk 2018-06-22 12:23:35.861672494 -0700 +++ new/make/lib/Lib-java.base.gmk 2018-06-22 12:23:35.602672501 -0700 @@ -51,7 +51,7 @@ $(call SET_SHARED_LIBRARY_ORIGIN), \ LDFLAGS_windows := -delayload:secur32.dll -delayload:iphlpapi.dll, \ LIBS_unix := -ljvm -ljava, \ - LIBS_linux := $(LIBDL) -lpthread, \ + LIBS_linux := $(LIBDL) -lpthread -lrdmacm, \ LIBS_solaris := -lnsl -lsocket $(LIBDL), \ LIBS_aix := $(LIBDL),\ LIBS_windows := ws2_32.lib jvm.lib secur32.lib iphlpapi.lib winhttp.lib \ @@ -78,7 +78,7 @@ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ LIBS_unix := -ljava -lnet, \ - LIBS_linux := -lpthread $(LIBDL), \ + LIBS_linux := -lpthread $(LIBDL) -lrdmacm, \ LIBS_solaris := -ljvm -lsocket -lposix4 $(LIBDL) \ -lsendfile, \ LIBS_aix := $(LIBDL), \ --- old/make/lib/Lib-jdk.net.gmk 2018-06-22 12:23:36.379672480 -0700 +++ new/make/lib/Lib-jdk.net.gmk 2018-06-22 12:23:36.153672486 -0700 @@ -32,12 +32,17 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBEXTNET, \ NAME := extnet, \ OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB), \ + CFLAGS := $(CFLAGS_JDKLIB) \ + -I$(SUPPORT_OUTPUTDIR)/headers/java.base \ + -I$(TOPDIR)/src/java.base/share/native/libnet \ + -I$(TOPDIR)/src/java.base/unix/native/libnet \ + -I$(TOPDIR)/src/java.base/share/native/libnio/ch \ + -I$(TOPDIR)/src/java.base/unix/native/libnio/ch, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ LIBS := -ljava, \ LIBS_solaris := -lsocket, \ - LIBS_linux := -ljvm, \ + LIBS_linux := -ljvm -lnet -lnio -lpthread -lrdmacm, \ )) $(BUILD_LIBEXTNET): $(call FindLib, java.base, java) --- old/make/test/JtregNativeJdk.gmk 2018-06-22 12:23:36.981672464 -0700 +++ new/make/test/JtregNativeJdk.gmk 2018-06-22 12:23:36.703672472 -0700 @@ -59,6 +59,7 @@ BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava ifeq ($(OPENJDK_TARGET_OS), linux) BUILD_JDK_JTREG_LIBRARIES_LIBS_libInheritedChannel := -ljava + BUILD_JDK_JTREG_LIBRARIES_LIBS_libRsocketTest := -ljava -lrdmacm else ifeq ($(OPENJDK_TARGET_OS), solaris) BUILD_JDK_JTREG_LIBRARIES_LIBS_libInheritedChannel := -ljava endif --- old/src/java.base/share/classes/java/net/InetAddressContainer.java 2018-06-22 12:23:37.561672448 -0700 +++ new/src/java.base/share/classes/java/net/InetAddressContainer.java 2018-06-22 12:23:37.273672456 -0700 @@ -26,5 +26,5 @@ package java.net; class InetAddressContainer { - InetAddress addr; + InetAddress addr; } --- old/src/java.base/share/classes/java/net/ServerSocket.java 2018-06-22 12:23:38.092672434 -0700 +++ new/src/java.base/share/classes/java/net/ServerSocket.java 2018-06-22 12:23:37.856672440 -0700 @@ -76,10 +76,11 @@ private boolean oldImpl = false; /** - * Package-private constructor to create a ServerSocket associated with - * the given SocketImpl. + * Creates a server socket with a user-specified SocketImpl. + * + * @param impl the user-specified SocketImpl */ - ServerSocket(SocketImpl impl) { + protected ServerSocket(SocketImpl impl) { this.impl = impl; impl.setServerSocket(this); } @@ -990,6 +991,7 @@ private static Set> options; private static boolean optionsSet = false; + private static boolean rdmaOptionsSet = false; /** * Returns a set of the socket options supported by this server socket. @@ -1004,7 +1006,9 @@ */ public Set> supportedOptions() { synchronized (ServerSocket.class) { - if (optionsSet) { + String currentImpl = impl.getClass().getName(); + boolean rdma = currentImpl.equals("rdma.ch.RdmaSocketImpl"); + if ((rdma && rdmaOptionsSet) || (!rdma && optionsSet)) { return options; } try { @@ -1013,7 +1017,13 @@ } catch (IOException e) { options = Collections.emptySet(); } - optionsSet = true; + if (rdma) { + rdmaOptionsSet = true; + optionsSet = false; + } else { + rdmaOptionsSet = false; + optionsSet = true; + } return options; } } --- old/src/java.base/share/classes/java/net/Socket.java 2018-06-22 12:23:38.640672419 -0700 +++ new/src/java.base/share/classes/java/net/Socket.java 2018-06-22 12:23:38.408672426 -0700 @@ -1791,6 +1791,7 @@ private static Set> options; private static boolean optionsSet = false; + private static boolean rdmaOptionsSet = false; /** * Returns a set of the socket options supported by this socket. @@ -1805,7 +1806,9 @@ */ public Set> supportedOptions() { synchronized (Socket.class) { - if (optionsSet) { + String currentImpl = impl.getClass().getName(); + boolean rdma = currentImpl.equals("rdma.ch.RdmaSocketImpl"); + if ((rdma && rdmaOptionsSet) || (!rdma && optionsSet)) { return options; } try { @@ -1814,7 +1817,13 @@ } catch (IOException e) { options = Collections.emptySet(); } - optionsSet = true; + if (rdma) { + rdmaOptionsSet = true; + optionsSet = false; + } else { + rdmaOptionsSet = false; + optionsSet = true; + } return options; } } --- old/src/java.base/share/classes/java/net/SocketImpl.java 2018-06-22 12:23:39.253672403 -0700 +++ new/src/java.base/share/classes/java/net/SocketImpl.java 2018-06-22 12:23:38.967672411 -0700 @@ -280,7 +280,11 @@ return localport; } - void setSocket(Socket soc) { + /** + * Set the Socket object + * @param soc the socket + */ + protected void setSocket(Socket soc) { this.socket = soc; } @@ -288,7 +292,11 @@ return socket; } - void setServerSocket(ServerSocket soc) { + /** + * Set the ServerSocket object + * @param soc the socket + */ + protected void setServerSocket(ServerSocket soc) { this.serverSocket = soc; } --- old/src/java.base/share/classes/module-info.java 2018-06-22 12:23:39.756672389 -0700 +++ new/src/java.base/share/classes/module-info.java 2018-06-22 12:23:39.527672395 -0700 @@ -223,7 +223,8 @@ jdk.jfr; exports sun.net to java.net.http, - jdk.naming.dns; + jdk.naming.dns + jdk.net; exports sun.net.ext to jdk.net; exports sun.net.dns to @@ -243,7 +244,8 @@ java.management, jdk.crypto.cryptoki, jdk.sctp, - jdk.unsupported; + jdk.unsupported, + jdk.net; exports sun.nio.cs to jdk.charsets; exports sun.nio.fs to @@ -261,7 +263,8 @@ java.sql.rowset; exports sun.security.action to java.desktop, - java.security.jgss; + java.security.jgss, + jdk.net; exports sun.security.internal.interfaces to jdk.crypto.cryptoki; exports sun.security.internal.spec to @@ -370,4 +373,7 @@ provides java.nio.file.spi.FileSystemProvider with jdk.internal.jrtfs.JrtFileSystemProvider; + + opens java.net to + jdk.net } --- old/src/java.base/share/classes/sun/nio/ch/IOUtil.java 2018-06-22 12:23:40.371672373 -0700 +++ new/src/java.base/share/classes/sun/nio/ch/IOUtil.java 2018-06-22 12:23:40.086672380 -0700 @@ -43,7 +43,7 @@ private IOUtil() { } // No instantiation - static int write(FileDescriptor fd, ByteBuffer src, long position, + public static int write(FileDescriptor fd, ByteBuffer src, long position, NativeDispatcher nd) throws IOException { @@ -123,7 +123,7 @@ return write(fd, bufs, 0, bufs.length, false, -1, nd); } - static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, + public static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, NativeDispatcher nd) throws IOException { @@ -216,7 +216,7 @@ } } - static int read(FileDescriptor fd, ByteBuffer dst, long position, + public static int read(FileDescriptor fd, ByteBuffer dst, long position, NativeDispatcher nd) throws IOException { @@ -286,7 +286,7 @@ return read(fd, bufs, 0, bufs.length, false, -1, nd); } - static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, + public static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, NativeDispatcher nd) throws IOException { --- old/src/java.base/share/classes/sun/nio/ch/Net.java 2018-06-22 12:23:40.931672358 -0700 +++ new/src/java.base/share/classes/sun/nio/ch/Net.java 2018-06-22 12:23:40.700672364 -0700 @@ -54,7 +54,7 @@ public class Net { - private Net() { } + protected Net() { } // unspecified protocol family static final ProtocolFamily UNSPEC = new ProtocolFamily() { @@ -79,7 +79,7 @@ /** * Tells whether dual-IPv4/IPv6 sockets should be used. */ - static boolean isIPv6Available() { + protected static boolean isIPv6Available() { if (!checkedIPv6) { isIPv6Available = isIPv6Available0(); checkedIPv6 = true; @@ -150,7 +150,7 @@ return (InetSocketAddress)sa; } - static void translateToSocketException(Exception x) + public static void translateToSocketException(Exception x) throws SocketException { if (x instanceof SocketException) @@ -180,7 +180,7 @@ throw new Error("Untranslated exception", nx); } - static void translateException(Exception x, + public static void translateException(Exception x, boolean unknownHostForUnresolved) throws IOException { @@ -196,7 +196,7 @@ translateToSocketException(x); } - static void translateException(Exception x) + public static void translateException(Exception x) throws IOException { translateException(x, false); @@ -205,7 +205,7 @@ /** * Returns the local address after performing a SecurityManager#checkConnect. */ - static InetSocketAddress getRevealedLocalAddress(InetSocketAddress addr) { + public static InetSocketAddress getRevealedLocalAddress(InetSocketAddress addr) { SecurityManager sm = System.getSecurityManager(); if (addr == null || sm == null) return addr; @@ -220,7 +220,7 @@ return addr; } - static String getRevealedLocalAddressAsString(InetSocketAddress addr) { + public static String getRevealedLocalAddressAsString(InetSocketAddress addr) { return System.getSecurityManager() == null ? addr.toString() : getLoopbackAddress(addr.getPort()).toString(); } --- old/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java 2018-06-22 12:23:41.514672342 -0700 +++ new/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java 2018-06-22 12:23:41.278672348 -0700 @@ -42,7 +42,7 @@ * Selector implementation based on poll */ -class PollSelectorImpl extends SelectorImpl { +public class PollSelectorImpl extends SelectorImpl { // initial capacity of poll array private static final int INITIAL_CAPACITY = 16; @@ -67,7 +67,7 @@ private final Object interruptLock = new Object(); private boolean interruptTriggered; - PollSelectorImpl(SelectorProvider sp) throws IOException { + protected PollSelectorImpl(SelectorProvider sp) throws IOException { super(sp); int size = pollArrayCapacity * SIZE_POLLFD; @@ -133,6 +133,14 @@ } /** + * Protected poll method allows different platform-specific + * native implementations + */ + protected int poll(long pollAddress, int numfds, int timeout) { + return poll0(pollAddress, numfds, timeout); + } + + /** * Process changes to the interest ops. */ private void processUpdateQueue() { @@ -377,7 +385,7 @@ return pollArray.getShort(offset); } - private static native int poll(long pollAddress, int numfds, int timeout); + private static native int poll0(long pollAddress, int numfds, int timeout); static { IOUtil.load(); --- old/src/java.base/unix/classes/sun/nio/ch/SocketDispatcher.java 2018-06-22 12:23:42.124672326 -0700 +++ new/src/java.base/unix/classes/sun/nio/ch/SocketDispatcher.java 2018-06-22 12:23:41.829672333 -0700 @@ -33,29 +33,29 @@ * for read and write operations. */ -class SocketDispatcher extends NativeDispatcher { +public class SocketDispatcher extends NativeDispatcher { - int read(FileDescriptor fd, long address, int len) throws IOException { + protected int read(FileDescriptor fd, long address, int len) throws IOException { return FileDispatcherImpl.read0(fd, address, len); } - long readv(FileDescriptor fd, long address, int len) throws IOException { + protected long readv(FileDescriptor fd, long address, int len) throws IOException { return FileDispatcherImpl.readv0(fd, address, len); } - int write(FileDescriptor fd, long address, int len) throws IOException { + protected int write(FileDescriptor fd, long address, int len) throws IOException { return FileDispatcherImpl.write0(fd, address, len); } - long writev(FileDescriptor fd, long address, int len) throws IOException { + protected long writev(FileDescriptor fd, long address, int len) throws IOException { return FileDispatcherImpl.writev0(fd, address, len); } - void close(FileDescriptor fd) throws IOException { + protected void close(FileDescriptor fd) throws IOException { FileDispatcherImpl.close0(fd); } - void preClose(FileDescriptor fd) throws IOException { + public void preClose(FileDescriptor fd) throws IOException { FileDispatcherImpl.preClose0(fd); } } --- old/src/java.base/unix/native/libnio/ch/PollSelectorImpl.c 2018-06-22 12:23:42.697672310 -0700 +++ new/src/java.base/unix/native/libnio/ch/PollSelectorImpl.c 2018-06-22 12:23:42.461672316 -0700 @@ -33,7 +33,7 @@ #include "sun_nio_ch_PollSelectorImpl.h" JNIEXPORT jint JNICALL -Java_sun_nio_ch_PollSelectorImpl_poll(JNIEnv *env, jclass clazz, +Java_sun_nio_ch_PollSelectorImpl_poll0(JNIEnv *env, jclass clazz, jlong address, jint numfds, jint timeout) { --- old/src/jdk.net/share/classes/jdk/net/Sockets.java 2018-06-22 12:23:43.443672290 -0700 +++ new/src/jdk.net/share/classes/jdk/net/Sockets.java 2018-06-22 12:23:43.202672297 -0700 @@ -26,12 +26,16 @@ package jdk.net; import java.net.*; +import java.nio.channels.*; import java.io.IOException; +import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import rdma.ch.RdmaPollSelectorProvider; +import rdma.ch.RdmaSocketImpl; import jdk.net.ExtendedSocketOptions.PlatformSocketOptions; /** @@ -375,4 +379,80 @@ ExtendedSocketOptions.TCP_KEEPINTERVAL)); } } + + /** + * Creates an unconnected RDMA socket + */ + public static Socket openRdmaSocket() throws IOException { + RdmaSocketImpl impl = new RdmaSocketImpl(); + Socket s = new Socket(impl) {}; + return s; + } + + /** + * Creates an unbound RDMA server socket + */ + public static ServerSocket openRdmaServerSocket() throws IOException { + SocketImpl impl = new RdmaSocketImpl(); + ServerSocket ss = new ServerSocket(impl) { + public Socket accept() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!isBound()) + throw new SocketException("Socket is not bound yet"); + + Socket s = openRdmaSocket(); + implAccept(s); + return s; + } + }; + return ss; + } + + /** + * Opens a RDMA socket channel. + * + * @return A new RDMA socket channel + * + * @throws IOException + * If an I/O error occurs + */ + public static SocketChannel openRdmaSocketChannel() throws IOException { + return RdmaPollSelectorProvider.provider().openSocketChannel(); + } + + /** + * Opens a RDMA server-socket channel. + * + *

The new channel is created by invoking the {@link + * java.nio.channels.spi.SelectorProvider#openServerSocketChannel + * openServerSocketChannel} method of the system-wide default {@link + * java.nio.channels.spi.SelectorProvider} object. + * + *

The new channel's socket is initially unbound; it must be bound to a + * specific address via one of its socket's {@link + * java.net.ServerSocket#bind(SocketAddress) bind} methods before + * connections can be accepted.

+ * + * @return A new RDMA server socket channel + * + * @throws IOException + * If an I/O error occurs + */ + public static ServerSocketChannel openRdmaServerSocketChannel() + throws IOException { + return RdmaPollSelectorProvider.provider().openServerSocketChannel(); + } + + /** + * Opens a RDMA selector. + * + * @return A new RDMA selector + * + * @throws IOException + * If an I/O error occurs + */ + public static Selector openRdmaSelector() throws IOException { + return RdmaPollSelectorProvider.provider().openSelector(); + } } --- old/src/jdk.net/share/classes/module-info.java 2018-06-22 12:23:44.387672265 -0700 +++ new/src/jdk.net/share/classes/module-info.java 2018-06-22 12:23:44.106672272 -0700 @@ -31,5 +31,7 @@ */ module jdk.net { exports jdk.net; + exports rdma.ch to + java.base; } --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/java.base/share/classes/sun/net/ext/RdmaSocketOptions.java 2018-06-22 12:23:44.878672251 -0700 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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.ext; + +import java.io.FileDescriptor; +import java.net.SocketException; +import java.net.SocketOption; +import java.util.Collections; +import java.util.Set; + +/** + * Defines the infrastructure to support RDMA socket options. + * + * Extended socket options are accessed through the jdk.net API, which is in + * the jdk.net module. + */ +public abstract class RdmaSocketOptions { + + private final Set> options; + + /** Tells whether or not the option is supported. */ + public final boolean isOptionSupported(SocketOption option) { + return options().contains(option); + } + + /** Return the, possibly empty, set of extended socket options available. */ + public final Set> options() { return options; } + + /** Sets the value of a socket option, for the given socket. */ + public abstract void setOption(FileDescriptor fd, SocketOption option, Object value) + throws SocketException; + + /** Returns the value of a socket option, for the given socket. */ + public abstract Object getOption(FileDescriptor fd, SocketOption option) + throws SocketException; + + protected RdmaSocketOptions(Set> options) { + this.options = options; + } + + private static volatile RdmaSocketOptions instance; + + public static final RdmaSocketOptions getInstance() { return instance; } + + /** Registers support for extended socket options. Invoked by the jdk.net module. */ + public static final void register(RdmaSocketOptions rdmaOptions) { + if (instance != null) + throw new InternalError("Attempting to reregister RDMA options"); + + instance = rdmaOptions; + } + + static { + try { + // If the class is present, it will be initialized which + // triggers registration of the extended socket options. + Class c = Class.forName("jdk.net.RdmaSocketOptions"); + } catch (ClassNotFoundException e) { + // the jdk.net module is not present => no extended socket options + instance = new NoRdmaSocketOptions(); + } + } + + static final class NoRdmaSocketOptions extends RdmaSocketOptions { + + NoRdmaSocketOptions() { + super(Collections.>emptySet()); + } + + @Override + public void setOption(FileDescriptor fd, SocketOption option, Object value) + throws SocketException + { + throw new UnsupportedOperationException( + "no extended options: " + option.name()); + } + + @Override + public Object getOption(FileDescriptor fd, SocketOption option) + throws SocketException + { + throw new UnsupportedOperationException( + "no extended options: " + option.name()); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/classes/jdk/net/LinuxRdmaSocketOptions.java 2018-06-22 12:23:45.518672234 -0700 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 jdk.net.RdmaSocketOptions.PlatformRdmaSocketOptions; + +class LinuxRdmaSocketOptions extends PlatformRdmaSocketOptions { + + public LinuxRdmaSocketOptions() { } + + @Override native void setSockOpt(int fd, int opt, int value) + throws SocketException; + + @Override native int getSockOpt(int fd, int opt) + throws SocketException; + + @Override native boolean rdmaSocketSupported(); + + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<>() { + public Void run() { + System.loadLibrary("extnet"); + return null; + } + }); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/classes/rdma/ch/LinuxRdmaSocketDispatcher.java 2018-06-22 12:23:46.071672219 -0700 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; +import java.io.*; + +public class LinuxRdmaSocketDispatcher + extends RdmaSocketDispatcher.PlatformRdmaSocketDispatcher +{ + protected int read(FileDescriptor fd, long address, int len) throws IOException { + return LinuxRdmaSocketDispatcherImpl.read0(fd, address, len); + } + + protected long readv(FileDescriptor fd, long address, int len) throws IOException { + return LinuxRdmaSocketDispatcherImpl.readv0(fd, address, len); + } + + protected int write(FileDescriptor fd, long address, int len) throws IOException { + return LinuxRdmaSocketDispatcherImpl.write0(fd, address, len); + } + + protected long writev(FileDescriptor fd, long address, int len) throws IOException { + return LinuxRdmaSocketDispatcherImpl.writev0(fd, address, len); + } + + protected void close(FileDescriptor fd) throws IOException { + LinuxRdmaSocketDispatcherImpl.close0(fd); + } + + public void preClose(FileDescriptor fd) throws IOException { + LinuxRdmaSocketDispatcherImpl.preClose0(fd); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/classes/rdma/ch/LinuxRdmaSocketDispatcherImpl.java 2018-06-22 12:23:46.650672204 -0700 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import sun.nio.ch.IOUtil; + +import jdk.internal.misc.JavaIOFileDescriptorAccess; +import jdk.internal.misc.SharedSecrets; + +public class LinuxRdmaSocketDispatcherImpl { + + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + System.loadLibrary("extnet"); + return null; + } + }); + IOUtil.load(); + init(); + } + + private static final JavaIOFileDescriptorAccess fdAccess = + SharedSecrets.getJavaIOFileDescriptorAccess(); + + LinuxRdmaSocketDispatcherImpl() { + } + + protected void close(FileDescriptor fd) throws IOException { + fdAccess.close(fd); + } + + protected void preClose(FileDescriptor fd) throws IOException { + preClose0(fd); + } + + // -- Native methods -- + + static native int read0(FileDescriptor fd, long address, int len) + throws IOException; + + static native long readv0(FileDescriptor fd, long address, int len) + throws IOException; + + static native int write0(FileDescriptor fd, long address, int len) + throws IOException; + + static native long writev0(FileDescriptor fd, long address, int len) + throws IOException; + + static native void close0(FileDescriptor fd) throws IOException; + + static native void preClose0(FileDescriptor fd) throws IOException; + + static native void init(); +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/classes/rdma/ch/LinuxRdmaSocketImpl.java 2018-06-22 12:23:47.193672189 -0700 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.IOException; +import java.net.SocketException; +import java.net.InetAddress; +import java.net.SocketImpl; +import sun.net.ext.RdmaSocketOptions; +import rdma.ch.RdmaSocketImpl.PlatformRdmaSocketImpl; + +class LinuxRdmaSocketImpl extends PlatformRdmaSocketImpl +{ + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<>() { + public Void run() { + System.loadLibrary("net"); + System.loadLibrary("extnet"); + return null; + } + }); + initProto(); + } + + public LinuxRdmaSocketImpl() { } + + static final RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + + private static volatile boolean checkedRdma; + private static volatile boolean isRdmaAvailable; + + boolean isRdmaAvailable() { + if (!checkedRdma) { + isRdmaAvailable = isRdmaAvailable0(); + checkedRdma = true; + } + return isRdmaAvailable; + } + + static native void initProto(); + + private static native boolean isRdmaAvailable0(); + + native void rdmaSocketCreate(boolean isServer, RdmaSocketImpl impl) throws IOException; + + native void rdmaSocketConnect(RdmaSocketImpl impl, InetAddress address, int port, int timeout) + throws IOException; + + native void rdmaSocketBind(RdmaSocketImpl impl, InetAddress address, int port) + throws IOException; + + native void rdmaSocketListen(RdmaSocketImpl impl, int count) throws IOException; + + native void rdmaSocketAccept(SocketImpl impl, RdmaSocketImpl s) throws IOException; + + native int rdmaSocketAvailable(RdmaSocketImpl impl) throws IOException; + + native void rdmaSocketClose(boolean useDeferredClose, RdmaSocketImpl impl) throws IOException; + + native void rdmaSocketShutdown(int howto, RdmaSocketImpl impl) throws IOException; + + native void rdmaSocketSetOption(RdmaSocketImpl impl, int cmd, boolean on, Object value) + throws SocketException; + + native int rdmaSocketGetOption(RdmaSocketImpl impl, int opt, Object iaContainerObj) throws SocketException; + + native void rdmaSocketSendUrgentData(RdmaSocketImpl impl, int data) throws IOException; +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/LinuxRdmaSocketDispatcherImpl.c 2018-06-22 12:23:47.769672174 -0700 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" +#include "nio_util.h" +#include "rdma_ch_LinuxRdmaSocketDispatcherImpl.h" +#include "java_lang_Long.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nio.h" + +static int preCloseFD = -1; /* File descriptor to which we dup other fd's + before closing them for real */ +static jfieldID fd_fdID; + +jint +convertReturnVal(JNIEnv *env, jint n, jboolean reading) +{ + if (n > 0) + return n; + else if (n == 0) { + if (reading) { + return IOS_EOF; + } else { + return 0; + } + } + else if (errno == EAGAIN) + return IOS_UNAVAILABLE; + else if (errno == EINTR) + return IOS_INTERRUPTED; + else { + const char *msg = reading ? "Read failed" : "Write failed"; + JNU_ThrowIOExceptionWithLastError(env, msg); + return IOS_THROWN; + } +} + +jlong +convertLongReturnVal(JNIEnv *env, jlong n, jboolean reading) +{ + if (n > 0) + return n; + else if (n == 0) { + if (reading) { + return IOS_EOF; + } else { + return 0; + } + } + else if (errno == EAGAIN) + return IOS_UNAVAILABLE; + else if (errno == EINTR) + return IOS_INTERRUPTED; + else { + const char *msg = reading ? "Read failed" : "Write failed"; + JNU_ThrowIOExceptionWithLastError(env, msg); + return IOS_THROWN; + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketDispatcherImpl_init(JNIEnv *env, jclass cl) +{ + CHECK_NULL(cl = (*env)->FindClass(env, "java/io/FileDescriptor")); + CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, cl, "fd", "I")); + + int sp[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) < 0) { + JNU_ThrowIOExceptionWithLastError(env, "socketpair failed"); + return; + } + preCloseFD = sp[0]; + rclose(sp[1]); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_LinuxRdmaSocketDispatcherImpl_read0(JNIEnv *env, jclass clazz, + jobject fdo, jlong address, jint len) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + void *buf = (void *)jlong_to_ptr(address); + return convertReturnVal(env, rread(fd, buf, len), JNI_TRUE); +} + +JNIEXPORT jlong JNICALL +Java_rdma_ch_LinuxRdmaSocketDispatcherImpl_readv0(JNIEnv *env, jclass clazz, + jobject fdo, jlong address, jint len) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + struct iovec *iov = (struct iovec *)jlong_to_ptr(address); + return convertLongReturnVal(env, rreadv(fd, iov, len), JNI_TRUE); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_LinuxRdmaSocketDispatcherImpl_write0(JNIEnv *env, jclass clazz, + jobject fdo, jlong address, jint len) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + void *buf = (void *)jlong_to_ptr(address); + return convertReturnVal(env, rwrite(fd, buf, len), JNI_FALSE); +} + +JNIEXPORT jlong JNICALL +Java_rdma_ch_LinuxRdmaSocketDispatcherImpl_writev0(JNIEnv *env, jclass clazz, + jobject fdo, jlong address, jint len) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + struct iovec *iov = (struct iovec *)jlong_to_ptr(address); + return convertLongReturnVal(env, rwritev(fd, iov, len), JNI_FALSE); +} + +static jlong +handle(JNIEnv *env, jlong rv, char *msg) +{ + if (rv >= 0) + return rv; + if (errno == EINTR) + return IOS_INTERRUPTED; + JNU_ThrowIOExceptionWithLastError(env, msg); + return IOS_THROWN; +} + +static void closeFileDescriptor(JNIEnv *env, int fd) { + if (fd != -1) { + int result = rclose(fd); + if (result < 0) + JNU_ThrowIOExceptionWithLastError(env, "Close failed"); + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo) +{ + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + closeFileDescriptor(env, fd); +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketDispatcherImpl_preClose0(JNIEnv *env, jclass clazz, jobject fdo) +{ +/* + jint fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (preCloseFD >= 0) { + if (dup2(preCloseFD, fd) < 0) + JNU_ThrowIOExceptionWithLastError(env, "dup2 failed"); + } +*/ +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/LinuxRdmaSocketImpl.c 2018-06-22 12:23:48.262672160 -0700 @@ -0,0 +1,784 @@ +/* + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 + +#include "jvm.h" +#include "rdma_util_md.h" + +#include "java_net_SocketOptions.h" + +#include "net_util.h" +#include "net_util_md.h" +#include "rdma_ch_LinuxRdmaSocketImpl.h" + +/************************************************************************ + * RdmaSocketImpl + */ + +static jfieldID IO_fd_fdID; + +jfieldID psi_fdID; +jfieldID psi_addressID; +jfieldID psi_ipaddressID; +jfieldID psi_portID; +jfieldID psi_localportID; +jfieldID psi_timeoutID; +jfieldID psi_trafficClassID; +jfieldID psi_serverSocketID; +jfieldID psi_fdLockID; +jfieldID psi_closePendingID; + + +/* + * file descriptor used for dup2 + */ +static int marker_fd = -1; + +#define SET_NONBLOCKING(fd) { \ + int flags = rfcntl(fd, F_GETFL); \ + flags |= O_NONBLOCK; \ + rfcntl(fd, F_SETFL, flags); \ +} + +#define SET_BLOCKING(fd) { \ + int flags = rfcntl(fd, F_GETFL); \ + flags &= ~O_NONBLOCK; \ + rfcntl(fd, F_SETFL, flags); \ +} + +static jclass socketExceptionCls; + +static int getMarkerFD() +{ + int sv[2]; + +#ifdef AF_UNIX + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { + return -1; + } +#else + return -1; +#endif + + rshutdown(sv[0], 2); + rclose(sv[1]); + + return sv[0]; +} + +static int getFD(JNIEnv *env, jobject this) { + jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); + CHECK_NULL_RETURN(fdObj, -1); + return (*env)->GetIntField(env, fdObj, IO_fd_fdID); +} + + +jfieldID +NET_GetFileDescriptorID(JNIEnv *env) +{ + jclass cls = (*env)->FindClass(env, "java/io/FileDescriptor"); + CHECK_NULL_RETURN(cls, NULL); + return (*env)->GetFieldID(env, cls, "fd", "I"); +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_initProto(JNIEnv *env, jclass cls) { + jclass clazz = (*env)->FindClass(env, "rdma/ch/RdmaSocketImpl"); + psi_fdID = (*env)->GetFieldID(env, clazz , "fd", + "Ljava/io/FileDescriptor;"); + CHECK_NULL(psi_fdID); + psi_addressID = (*env)->GetFieldID(env, clazz, "address", + "Ljava/net/InetAddress;"); + CHECK_NULL(psi_addressID); + psi_timeoutID = (*env)->GetFieldID(env, clazz, "timeout", "I"); + CHECK_NULL(psi_timeoutID); + psi_trafficClassID = (*env)->GetFieldID(env, clazz, "trafficClass", "I"); + CHECK_NULL(psi_trafficClassID); + psi_portID = (*env)->GetFieldID(env, clazz, "port", "I"); + CHECK_NULL(psi_portID); + psi_localportID = (*env)->GetFieldID(env, clazz, "localport", "I"); + CHECK_NULL(psi_localportID); + psi_serverSocketID = (*env)->GetFieldID(env, clazz, "serverSocket", + "Ljava/net/ServerSocket;"); + CHECK_NULL(psi_serverSocketID); + + IO_fd_fdID = NET_GetFileDescriptorID(env); + CHECK_NULL(IO_fd_fdID); + + initInetAddressIDs(env); + JNU_CHECK_EXCEPTION(env); + marker_fd = getMarkerFD(); +} + +JNIEXPORT jboolean JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_isRdmaAvailable0(JNIEnv *env, jclass cls) { + return rdma_supported(); +} + +void +NET_SetTrafficClass(SOCKETADDRESS *sa, int trafficClass) { + if (sa->sa.sa_family == AF_INET6) { + sa->sa6.sin6_flowinfo = htonl((trafficClass & 0xff) << 20); + } +} + +/* + * Class: jdk_net_LinuxRdmaSocketImpl + * Method: rdmaSocketCreate + * Signature: (Z)V */ +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketCreate(JNIEnv *env, jobject this, + jboolean stream, jobject impl) { + jobject fdObj, ssObj; + int fd; + int type = (stream ? SOCK_STREAM : SOCK_DGRAM); + int domain = ipv6_available() ? AF_INET6 : AF_INET; + + if (socketExceptionCls == NULL) { + jclass c = (*env)->FindClass(env, "java/net/SocketException"); + CHECK_NULL(c); + socketExceptionCls = (jclass)(*env)->NewGlobalRef(env, c); + CHECK_NULL(socketExceptionCls); + } + + fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + + if (fdObj == NULL) { + (*env)->ThrowNew(env, socketExceptionCls, "null fd object"); + return; + } + + if ((fd = rsocket(domain, type, 0)) == -1) { + NET_ThrowNew(env, errno, "can't create socket"); + return; + } + if (domain == AF_INET6) { + int arg = 0; + if (rsetsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, + sizeof(int)) < 0) { + NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6"); + rclose(fd); + return; + } + } + + ssObj = (*env)->GetObjectField(env, impl, psi_serverSocketID); + if (ssObj != NULL) { + int arg = 1; + SET_NONBLOCKING(fd); + if (RDMA_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, + sizeof(arg)) < 0) { + NET_ThrowNew(env, errno, "cannot set SO_REUSEADDR"); + rclose(fd); + return; + } + } + (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketConnect(JNIEnv *env, jobject this, jobject impl, + jobject iaObj, jint port, + jint timeout) +{ + jint localport = (*env)->GetIntField(env, impl, psi_localportID); + int len = 0; + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + + jobject fdLock; + + jint trafficClass = (*env)->GetIntField(env, impl, psi_trafficClassID); + + jint fd; + + SOCKETADDRESS sa; + int connect_rv = -1; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (IS_NULL(iaObj)) { + JNU_ThrowNullPointerException(env, "inet address argument null."); + return; + } + + if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, &len, + JNI_TRUE) != 0) { + return; + } + + if (trafficClass != 0 && ipv6_available()) { + NET_SetTrafficClass(&sa, trafficClass); + } + + if (timeout <= 0) { + connect_rv = RDMA_Connect(fd, &sa.sa, len); + } else { + SET_NONBLOCKING(fd); + + connect_rv = rconnect(fd, &sa.sa, len); + + if (connect_rv != 0) { + socklen_t optlen; + jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC; + jlong prevNanoTime = JVM_NanoTime(env, 0); + + if (errno != EINPROGRESS) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException", + "connect failed"); + SET_BLOCKING(fd); + return; + } + + while (1) { + jlong newNanoTime; + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLOUT; + + errno = 0; + + connect_rv = RDMA_Poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC); + + if (connect_rv >= 0) { + break; + } + if (errno != EINTR) { + break; + } + + newNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= (newNanoTime - prevNanoTime); + if (nanoTimeout < NET_NSEC_PER_MSEC) { + connect_rv = 0; + break; + } + prevNanoTime = newNanoTime; + + } + + if (connect_rv == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "connect timed out"); + + SET_BLOCKING(fd); + rshutdown(fd, 2); + return; + } + + optlen = sizeof(connect_rv); + if (rgetsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv, + &optlen) <0) { + connect_rv = errno; + } + } + + SET_BLOCKING(fd); + + if (connect_rv != 0) { + errno = connect_rv; + connect_rv = -1; + } + } + + if (connect_rv < 0) { + if (connect_rv == -1 && errno == EINVAL) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Invalid argument or cannot assign requested address"); + return; + } +#if defined(EPROTO) + if (errno == EPROTO) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ProtocolException", + "Protocol error"); + return; + } +#endif + if (errno == ECONNREFUSED) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException", + "Connection refused"); + } else if (errno == ETIMEDOUT) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException", + "Connection timed out"); + } else if (errno == EHOSTUNREACH) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "NoRouteToHostException", + "Host unreachable"); + } else if (errno == EADDRNOTAVAIL) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "NoRouteToHostException", + "Address not available"); + } else if ((errno == EISCONN) || (errno == EBADF)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "connect failed"); + } + return; + } + + (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); + + (*env)->SetObjectField(env, impl, psi_addressID, iaObj); + (*env)->SetIntField(env, impl, psi_portID, port); + + if (localport == 0) { + socklen_t slen = sizeof(SOCKETADDRESS); + if (rgetsockname(fd, &sa.sa, &slen) == -1) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error getting socket name"); + } else { + localport = NET_GetPortFromSockaddr(&sa); + (*env)->SetIntField(env, impl, psi_localportID, localport); + } + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketBind(JNIEnv *env, jobject this, jobject impl, + jobject iaObj, jint localport) { + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + int fd; + int len = 0; + SOCKETADDRESS sa; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (IS_NULL(iaObj)) { + JNU_ThrowNullPointerException(env, "iaObj is null."); + return; + } + + if (NET_InetAddressToSockaddr(env, iaObj, localport, &sa, + &len, JNI_TRUE) != 0) { + return; + } + + if (RDMA_Bind(fd, &sa, len) < 0) { + if (errno == EADDRINUSE || errno == EADDRNOTAVAIL || + errno == EPERM || errno == EACCES) { + JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "BindException", + "Bind failed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Bind failed"); + } + return; + } + (*env)->SetObjectField(env, impl, psi_addressID, iaObj); + + if (localport == 0) { + socklen_t slen = sizeof(SOCKETADDRESS); + if (rgetsockname(fd, &sa.sa, &slen) == -1) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error getting socket name"); + return; + } + localport = NET_GetPortFromSockaddr(&sa); + (*env)->SetIntField(env, impl, psi_localportID, localport); + } else { + (*env)->SetIntField(env, impl, psi_localportID, localport); + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketListen(JNIEnv *env, jobject this, jobject impl, + jint count) +{ + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + int fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + + if (count == 0x7fffffff) + count -= 1; + + int rv = rlisten(fd, count); + if (rv == -1) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Listen failed"); + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketAccept(JNIEnv *env, jobject this, jobject socket, jobject impl) +{ + int port; + jint timeout = (*env)->GetIntField(env, impl, psi_timeoutID); + jlong prevNanoTime = 0; + jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC; + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + + jobject socketFdObj; + jobject socketAddressObj; + + jint fd; + + jint newfd; + + SOCKETADDRESS sa; + socklen_t slen = sizeof(SOCKETADDRESS); + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (IS_NULL(socket)) { + JNU_ThrowNullPointerException(env, "socket is null"); + return; + } + + for (;;) { + int ret; + jlong currNanoTime; + + if (prevNanoTime == 0 && nanoTimeout > 0) { + prevNanoTime = JVM_NanoTime(env, 0); + } + + if (timeout <= 0) { + ret = RDMA_Timeout(env, fd, -1, 0); + } else { + ret = RDMA_Timeout(env, fd, nanoTimeout / NET_NSEC_PER_MSEC, prevNanoTime); + } + + if (ret == 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "Accept timed out"); + return; + } else if (ret == -1) { + if (errno == EBADF) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + } else if (errno == ENOMEM) { + JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Accept failed"); + } + return; + } + + newfd = RDMA_Accept(fd, &sa.sa, &slen); + + if (newfd >= 0) { + SET_BLOCKING(newfd); + break; + } + + if (!(errno == ECONNABORTED || errno == EWOULDBLOCK)) { + break; + } + + if (nanoTimeout >= NET_NSEC_PER_MSEC) { + currNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= (currNanoTime - prevNanoTime); + if (nanoTimeout < NET_NSEC_PER_MSEC) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", + "Accept timed out"); + return; + } + prevNanoTime = currNanoTime; + } + } + + if (newfd < 0) { + if (newfd == -2) { + JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException", + "operation interrupted"); + } else { + if (errno == EINVAL) { + errno = EBADF; + } + if (errno == EBADF) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Accept failed"); + } + } + return; + } + + socketAddressObj = NET_SockaddrToInetAddress(env, &sa, &port); + if (socketAddressObj == NULL) { + rclose(newfd); + return; + } + + socketFdObj = (*env)->GetObjectField(env, socket, psi_fdID); + (*env)->SetIntField(env, socketFdObj, IO_fd_fdID, newfd); + (*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj); + (*env)->SetIntField(env, socket, psi_portID, port); + port = (*env)->GetIntField(env, impl, psi_localportID); + (*env)->SetIntField(env, socket, psi_localportID, port); +} + + +JNIEXPORT jint JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketAvailable(JNIEnv *env, jobject this, jobject impl) { + jint ret = -1; + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + jint fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return -1; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (RDMA_SocketAvailable(fd, &ret) == 0){ + if (errno == ECONNRESET) { + JNU_ThrowByName(env, "sun/net/ConnectionResetException", ""); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "ioctl FIONREAD failed"); + } + } + return ret; +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketClose(JNIEnv *env, jobject this, + jboolean useDeferredClose, jobject impl) { + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + jint fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket already closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + if (fd != -1) { + if (useDeferredClose && marker_fd >= 0) { + RDMA_Dup2(marker_fd, fd); + } else { + (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1); + RDMA_SocketClose(fd); + } + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketShutdown(JNIEnv *env, jobject this, + jint howto, jobject impl) +{ + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + jint fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "socket already closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + } + rshutdown(fd, howto); +} + + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketSetOption + (JNIEnv *env, jobject this, jobject impl, jint cmd, jboolean on, jobject value) +{ + int fd; + int level, optname, optlen; + union { + int i; + struct linger ling; + } optval; + + fd = getFD(env, impl); + if (fd < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return; + } + + if (cmd == java_net_SocketOptions_SO_TIMEOUT) { + return; + } + + if (RDMA_MapSocketOption(cmd, &level, &optname)) { + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); + return; + } + + switch (cmd) { + case java_net_SocketOptions_SO_SNDBUF : + case java_net_SocketOptions_SO_RCVBUF : + case java_net_SocketOptions_SO_LINGER : + { + jclass cls; + jfieldID fid; + + cls = (*env)->FindClass(env, "java/lang/Integer"); + CHECK_NULL(cls); + fid = (*env)->GetFieldID(env, cls, "value", "I"); + CHECK_NULL(fid); + + if (cmd == java_net_SocketOptions_SO_LINGER) { + if (on) { + optval.ling.l_onoff = 1; + optval.ling.l_linger = (*env)->GetIntField(env, value, fid); + } else { + optval.ling.l_onoff = 0; + optval.ling.l_linger = 0; + } + optlen = sizeof(optval.ling); + } else { + optval.i = (*env)->GetIntField(env, value, fid); + optlen = sizeof(optval.i); + } + + break; + } + + default : + optval.i = (on ? 1 : 0); + optlen = sizeof(optval.i); + + } + if (RDMA_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error setting socket option"); + } +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketGetOption + (JNIEnv *env, jobject this, jobject impl, jint cmd, jobject iaContainerObj) +{ + int fd; + int level, optname, optlen; + union { + int i; + struct linger ling; + } optval; + + fd = getFD(env, impl); + if (fd < 0) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket closed"); + return -1; + } + + if (cmd == java_net_SocketOptions_SO_BINDADDR) { + SOCKETADDRESS sa; + socklen_t len = sizeof(SOCKETADDRESS); + int port; + jobject iaObj; + jclass iaCntrClass; + jfieldID iaFieldID; + + if (rgetsockname(fd, &sa.sa, &len) < 0) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error getting socket name"); + return -1; + } + iaObj = NET_SockaddrToInetAddress(env, &sa, &port); + CHECK_NULL_RETURN(iaObj, -1); + + iaCntrClass = (*env)->GetObjectClass(env, iaContainerObj); + iaFieldID = (*env)->GetFieldID(env, iaCntrClass, "addr", "Ljava/net/InetAddress;"); + CHECK_NULL_RETURN(iaFieldID, -1); + (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj); + return 0; + } + + if (RDMA_MapSocketOption(cmd, &level, &optname)) { + JNU_ThrowByName(env, "java/net/SocketException", "Invalid option"); + return -1; + } + + /* + * Args are int except for SO_LINGER + */ + if (cmd == java_net_SocketOptions_SO_LINGER) { + optlen = sizeof(optval.ling); + } else { + optlen = sizeof(optval.i); + } + + if (RDMA_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) { + JNU_ThrowByNameWithMessageAndLastError + (env, JNU_JAVANETPKG "SocketException", "Error getting socket option"); + return -1; + } + + switch (cmd) { + case java_net_SocketOptions_SO_LINGER: + return (optval.ling.l_onoff ? optval.ling.l_linger: -1); + + case java_net_SocketOptions_SO_SNDBUF: + case java_net_SocketOptions_SO_RCVBUF: + return optval.i; + + default : + return (optval.i == 0) ? -1 : 1; + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketSendUrgentData(JNIEnv *env, jobject this, jobject impl, + jint data) { + jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID); + int n, fd; + unsigned char d = data & 0xFF; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd == -1) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } + + } + n = RDMA_Send(fd, (char *)&d, 1, MSG_OOB); + if (n == -1) { + JNU_ThrowIOExceptionWithLastError(env, "Write failed"); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/LinuxRdmaSocketOptions.c 2018-06-22 12:23:48.790672146 -0700 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 "jdk_net_LinuxRdmaSocketOptions.h" +#include "rdma_util_md.h" +#include "jni_util.h" + +void throwByNameWithLastError + (JNIEnv *env, const char *name, const char *defaultDetail) +{ + char defaultMsg[255]; + sprintf(defaultMsg, "errno: %d, %s", errno, defaultDetail); + JNU_ThrowByNameWithLastError(env, name, defaultMsg); +} + +JNIEXPORT void JNICALL Java_jdk_net_LinuxRdmaSocketOptions_setSockOpt + (JNIEnv *env, jobject unused, jint fd, jint opt, jint value) +{ + int optname; + int level = SOL_RDMA; + RDMA_MapSocketOption(opt, &level, &optname); + int len = sizeof(value); + int rv = RDMA_SetSockOpt(fd, level, optname, (char*)&value, len); + if (rv < 0) { + if (errno == ENOPROTOOPT) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported RDMA socket option"); + } else if (errno == EACCES || errno == EPERM) { + JNU_ThrowByName(env, "java/net/SocketException", "Permission denied"); + } else { + throwByNameWithLastError(env, "java/net/SocketException", + "set RDMA option failed"); + } + } +} + +JNIEXPORT jint JNICALL Java_jdk_net_LinuxRdmaSocketOptions_getSockOpt + (JNIEnv *env, jobject unused, jint fd, jint opt) +{ + int optname; + int level = SOL_RDMA; + RDMA_MapSocketOption(opt, &level, &optname); + int value; + int len = sizeof(value); + int rv = RDMA_GetSockOpt(fd, level, optname, (char*)&value, &len); + + if (rv < 0) { + if (errno == ENOPROTOOPT) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported RDMA socket option"); + } else if (errno == EACCES || errno == EPERM) { + JNU_ThrowByName(env, "java/net/SocketException", "Permission denied"); + } else { + throwByNameWithLastError(env, "java/net/SocketException", + "get RDMA option failed"); + } + return -1; + } + return value; +} + +JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxRdmaSocketOptions_rdmaSocketSupported + (JNIEnv *env, jobject unused) +{ + int rv, s; + + s = rsocket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + return JNI_FALSE; + } + rclose(s); + return JNI_TRUE; +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaNet.c 2018-06-22 12:23:49.383672130 -0700 @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 +#include +#include +#include +#include +#include +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" +#include "sun_nio_ch_Net.h" +#include "net_util.h" +#include "net_util_md.h" +#include "nio_util.h" +#include "nio.h" +#include "rdma_util_md.h" + +#ifndef IP_MULTICAST_ALL + #define IP_MULTICAST_ALL 49 +#endif + +#define COPY_INET6_ADDRESS(env, source, target) \ + (*env)->GetByteArrayRegion(env, source, 0, 16, target) + +static jfieldID fd_fdID; + +JNIEXPORT jboolean JNICALL +Java_rdma_ch_RdmaNet_isRdmaAvailable0(JNIEnv *env, jclass cls) { + return rdma_supported(); +} + +static int +configureBlocking(int fd, jboolean blocking) +{ + int flags = rfcntl(fd, F_GETFL); + int newflags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); + + return (flags == newflags) ? 0 : rfcntl(fd, F_SETFL, newflags); +} + +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaNet_configureBlocking(JNIEnv *env, jclass clazz, + jobject fdo, jboolean blocking) +{ + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (configureBlocking(fd, blocking) < 0) + JNU_ThrowIOExceptionWithLastError(env, "Configure blocking failed"); +} + +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaNet_initIDs(JNIEnv *env, jclass clazz) +{ + CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor")); + CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I")); + initInetAddressIDs(env); +} + +JNIEXPORT jboolean JNICALL +Java_rdma_ch_RdmaNet_isIPv6Available0(JNIEnv* env, jclass cl) +{ + return (ipv6_available()) ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL +Java_rdma_ch_RdmaNet_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl) +{ + return JNI_TRUE; +} + +JNIEXPORT jboolean JNICALL +Java_rdma_ch_RdmaNet_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl) +{ + return JNI_FALSE; +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaNet_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, + jboolean stream, jboolean reuse, jboolean ignored) +{ + int fd; + int type = (stream ? SOCK_STREAM : SOCK_DGRAM); + int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET; + + fd = rsocket(domain, type, 0); + if (fd < 0) { + return handleSocketError(env, errno); + } + + if (reuse) { + int arg = 1; + if (rsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, + sizeof(arg)) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "Unable to set SO_REUSEADDR"); + rclose(fd); + return -1; + } + } + + return fd; +} + +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaNet_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6, + jboolean useExclBind, jobject iao, int port) +{ + SOCKETADDRESS sa; + int sa_len = 0; + int rv = 0; + + if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, + preferIPv6) != 0) { + return; + } + + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + rv = RDMA_Bind(fd, &sa, sa_len); + if (rv != 0) { + handleSocketError(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaNet_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog) +{ + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (rlisten(fd, backlog) < 0) + handleSocketError(env, errno); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaNet_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, + jobject fdo, jobject iao, jint port) +{ + SOCKETADDRESS sa; + int sa_len = 0; + int rv; + + if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, + preferIPv6) != 0) { + return IOS_THROWN; + } + + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + + rv = rconnect(fd, &sa.sa, sa_len); + if (rv != 0) { + if (errno == EINPROGRESS) { + return IOS_UNAVAILABLE; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + } + return handleSocketError(env, errno); + } + return 1; +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaNet_localPort(JNIEnv *env, jclass clazz, jobject fdo) +{ + SOCKETADDRESS sa; + socklen_t sa_len = sizeof(SOCKETADDRESS); + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (rgetsockname(fd, &sa.sa, &sa_len) < 0) { + handleSocketError(env, errno); + return -1; + } + return NET_GetPortFromSockaddr(&sa); +} + +JNIEXPORT jobject JNICALL +Java_rdma_ch_RdmaNet_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo) +{ + SOCKETADDRESS sa; + socklen_t sa_len = sizeof(SOCKETADDRESS); + int port; + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (rgetsockname(fd, &sa.sa, &sa_len) < 0) { + handleSocketError(env, errno); + return NULL; + } + return NET_SockaddrToInetAddress(env, &sa, &port); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaNet_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt) +{ + int result; + struct linger linger; + u_char carg; + void *arg; + socklen_t arglen; + int n; + + arg = (void *)&result; + arglen = sizeof(result); + + if (level == SOL_SOCKET && opt == SO_LINGER) { + arg = (void *)&linger; + arglen = sizeof(linger); + } + + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (mayNeedConversion) { + n = RDMA_GetSockOpt(fd, level, opt, arg, (int*)&arglen); + } else { + n = rgetsockopt(fd, level, opt, arg, &arglen); + } + if (n < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "rdma.ch.RdmaNet.getIntOption"); + return -1; + } + + if (level == SOL_SOCKET && opt == SO_LINGER) + return linger.l_onoff ? (jint)linger.l_linger : (jint)-1; + + return (jint)result; +} + +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaNet_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, + jint opt, jint arg, jboolean isIPv6) +{ + int result; + struct linger linger; + u_char carg; + void *parg; + socklen_t arglen; + int n; + + parg = (void*)&arg; + arglen = sizeof(arg); + + if (level == SOL_SOCKET && opt == SO_LINGER) { + parg = (void *)&linger; + arglen = sizeof(linger); + if (arg >= 0) { + linger.l_onoff = 1; + linger.l_linger = arg; + } else { + linger.l_onoff = 0; + linger.l_linger = 0; + } + } + + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if (mayNeedConversion) { + n = RDMA_SetSockOpt(fd, level, opt, parg, arglen); + } else { + n = rsetsockopt(fd, level, opt, parg, arglen); + } + if (n < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "rdma.ch.RdmaNet.setIntOption"); + } +} + +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaNet_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow) +{ + int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SHUT_RD : + (jhow == sun_nio_ch_Net_SHUT_WR) ? SHUT_WR : SHUT_RDWR; + int fd = (*env)->GetIntField(env, fdo, fd_fdID); + if ((rshutdown(fd, how) < 0) && (errno != ENOTCONN)) + handleSocketError(env, errno); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaNet_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout) +{ + struct pollfd pfd; + int rv; + pfd.fd = (*env)->GetIntField(env, fdo, fd_fdID); + pfd.events = events; + if (timeout < -1) { + timeout = -1; + } else if (timeout > INT_MAX) { + timeout = INT_MAX; + } + rv = rpoll(&pfd, 1, (int)timeout); + + if (rv >= 0) { + return pfd.revents; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + } else { + handleSocketError(env, errno); + return IOS_THROWN; + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaPollSelectorImpl.c 2018-06-22 12:23:49.920672116 -0700 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" +#include "nio.h" +#include "rdma_ch_RdmaPollSelectorImpl.h" + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaPollSelectorImpl_poll0(JNIEnv *env, jclass clazz, + jlong address, jint numfds, + jint timeout) +{ + struct pollfd *a; + int res; + + a = (struct pollfd *) jlong_to_ptr(address); + res = rpoll(a, numfds, timeout); + if (res < 0) { + if (errno == EINTR) { + return IOS_INTERRUPTED; + } else { + JNU_ThrowIOExceptionWithLastError(env, "poll failed"); + return IOS_THROWN; + } + } + return (jint) res; +} + --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaServerSocketChannelImpl.c 2018-06-22 12:23:50.512672100 -0700 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 +#include +#include +#include +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "net_util.h" +#include "jvm.h" +#include "jlong.h" +#include "rdma_ch_RdmaServerSocketChannelImpl.h" +#include "nio.h" +#include "nio_util.h" + + +static jfieldID fd_fdID; /* java.io.FileDescriptor.fd */ +static jclass isa_class; /* java.net.InetSocketAddress */ +static jmethodID isa_ctorID; /* .InetSocketAddress(InetAddress, int) */ + + +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaServerSocketChannelImpl_initIDs(JNIEnv *env, jclass c) +{ + jclass cls; + + cls = (*env)->FindClass(env, "java/io/FileDescriptor"); + CHECK_NULL(cls); + fd_fdID = (*env)->GetFieldID(env, cls, "fd", "I"); + CHECK_NULL(fd_fdID); + + cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); + CHECK_NULL(cls); + isa_class = (*env)->NewGlobalRef(env, cls); + if (isa_class == NULL) { + JNU_ThrowOutOfMemoryError(env, NULL); + return; + } + isa_ctorID = (*env)->GetMethodID(env, cls, "", + "(Ljava/net/InetAddress;I)V"); + CHECK_NULL(isa_ctorID); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaServerSocketChannelImpl_accept0(JNIEnv *env, jobject this, + jobject ssfdo, jobject newfdo, + jobjectArray isaa) +{ + jint ssfd = (*env)->GetIntField(env, ssfdo, fd_fdID); + jint newfd; + SOCKETADDRESS sa; + socklen_t sa_len = sizeof(SOCKETADDRESS); + jobject remote_ia = 0; + jobject isa; + jint remote_port = 0; + + /* + * accept connection but ignore ECONNABORTED indicating that + * a connection was eagerly accepted but was reset before + * accept() was called. + */ + for (;;) { + newfd = raccept(ssfd, &sa.sa, &sa_len); + if (newfd >= 0) { + break; + } + if (errno != ECONNABORTED) { + break; + } + /* ECONNABORTED => restart accept */ + } + + if (newfd < 0) { + if (errno == EAGAIN) + return IOS_UNAVAILABLE; + if (errno == EINTR) + return IOS_INTERRUPTED; + JNU_ThrowIOExceptionWithLastError(env, "Accept failed"); + return IOS_THROWN; + } + + (*env)->SetIntField(env, newfdo, fd_fdID, newfd); + remote_ia = NET_SockaddrToInetAddress(env, &sa, (int *)&remote_port); + CHECK_NULL_RETURN(remote_ia, IOS_THROWN); + isa = (*env)->NewObject(env, isa_class, isa_ctorID, remote_ia, remote_port); + CHECK_NULL_RETURN(isa, IOS_THROWN); + (*env)->SetObjectArrayElement(env, isaa, 0, isa); + return 1; +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaSocketChannelImpl.c 2018-06-22 12:23:51.138672083 -0700 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "jni.h" +#include "jni_util.h" +#include "net_util.h" +#include "jvm.h" +#include "jlong.h" +#include "rdma_ch_RdmaSocketChannelImpl.h" +#include "nio_util.h" +#include "nio.h" + +static jfieldID fd_fdID; + +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaSocketChannelImpl_initIDs(JNIEnv *env, jclass clazz) +{ + CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor")); + CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I")); +} + +jint fdVal(JNIEnv *env, jobject fdo) +{ + return (*env)->GetIntField(env, fdo, fd_fdID); +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaSocketChannelImpl_checkConnect(JNIEnv *env, jobject this, + jobject fdo, jboolean block) +{ + int error = 0; + socklen_t n = sizeof(int); + jint fd = fdVal(env, fdo); + int result = 0; + struct pollfd poller; + + poller.fd = fd; + poller.events = POLLOUT; + poller.revents = 0; + result = rpoll(&poller, 1, block ? -1 : 0); + + if (result < 0) { + if (errno == EINTR) { + return IOS_INTERRUPTED; + } else { + JNU_ThrowIOExceptionWithLastError(env, "poll failed"); + return IOS_THROWN; + } + } + if (!block && (result == 0)) + return IOS_UNAVAILABLE; + + if (result > 0) { + errno = 0; + result = rgetsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n); + if (result < 0) { + return handleSocketError(env, errno); + } else if (error) { + return handleSocketError(env, error); + } else if ((poller.revents & POLLHUP) != 0) { + return handleSocketError(env, ENOTCONN); + } + // connected + return 1; + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaSocketChannelImpl_sendOutOfBandData(JNIEnv* env, jclass this, + jobject fdo, jbyte b) +{ + int n = rsend(fdVal(env, fdo), (const void*)&b, 1, MSG_OOB); + return convertReturnVal(env, n, JNI_FALSE); +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaSocketInputStream.c 2018-06-22 12:23:51.753672066 -0700 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 +#include +#include + +#include "jvm.h" +#include "net_util.h" +#include "rdma_util_md.h" + +#include "rdma_ch_RdmaSocketInputStream.h" + +static jfieldID IO_fd_fdID; + +/* + * Class: jdk_net_RdmaSocketInputStream + * Method: init + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaSocketInputStream_init(JNIEnv *env, jclass cls) { + IO_fd_fdID = NET_GetFileDescriptorID(env); +} + +static int RDMA_ReadWithTimeout(JNIEnv *env, int fd, char *bufP, int len, long timeout) { + int result = 0; + jlong prevNanoTime = JVM_NanoTime(env, 0); + jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC; + while (nanoTimeout >= NET_NSEC_PER_MSEC) { + result = RDMA_Timeout(env, fd, nanoTimeout / NET_NSEC_PER_MSEC, prevNanoTime); + if (result <= 0) { + if (result == 0) { + JNU_ThrowByName(env, "java/net/SocketTimeoutException", "Read timed out"); + } else if (result == -1) { + if (errno == EBADF) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + } else if (errno == ENOMEM) { + JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, "java/net/SocketException", "select/poll failed"); + } + } + return -1; + } + result = RDMA_NonBlockingRead(fd, bufP, len); + if (result == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) { + jlong newtNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= newtNanoTime - prevNanoTime; + if (nanoTimeout >= NET_NSEC_PER_MSEC) { + prevNanoTime = newtNanoTime; + } + } else { + break; + } + } + return result; +} + +/* + * Class: jdk_net_RdmaSocketInputStream + * Method: rdmaSocketRead0 + * Signature: (Ljava/io/FileDescriptor;[BIII)I + */ +JNIEXPORT jint JNICALL +Java_rdma_ch_RdmaSocketInputStream_rdmaSocketRead0(JNIEnv *env, jobject this, + jobject fdObj, jbyteArray data, + jint off, jint len, jint timeout) +{ + char BUF[MAX_BUFFER_LEN]; + char *bufP; + jint fd, nread; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, "java/net/SocketException", + "Socket closed"); + return -1; + } + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd == -1) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return -1; + } + + if (len > MAX_BUFFER_LEN) { + if (len > MAX_HEAP_BUFFER_LEN) { + len = MAX_HEAP_BUFFER_LEN; + } + bufP = (char *)malloc((size_t)len); + if (bufP == NULL) { + bufP = BUF; + len = MAX_BUFFER_LEN; + } + } else { + bufP = BUF; + } + if (timeout) { + nread = RDMA_ReadWithTimeout(env, fd, bufP, len, timeout); + if ((*env)->ExceptionCheck(env)) { + if (bufP != BUF) { + free(bufP); + } + return nread; + } + } else { + nread = RDMA_Read(fd, bufP, len); + } + + if (nread <= 0) { + if (nread < 0) { + + switch (errno) { + case ECONNRESET: + case EPIPE: + JNU_ThrowByName(env, "sun/net/ConnectionResetException", + "Connection reset"); + break; + + case EBADF: + JNU_ThrowByName(env, "java/net/SocketException", + "Socket closed"); + break; + + case EINTR: + JNU_ThrowByName(env, "java/io/InterruptedIOException", + "Operation interrupted"); + break; + default: + JNU_ThrowByNameWithMessageAndLastError + (env, "java/net/SocketException", "Read failed"); + } + } + } else { + (*env)->SetByteArrayRegion(env, data, off, nread, (jbyte *)bufP); + } + + if (bufP != BUF) { + free(bufP); + } + return nread; +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/RdmaSocketOutputStream.c 2018-06-22 12:23:52.368672050 -0700 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 +#include +#include + +#include "net_util.h" +#include "rdma_util_md.h" + +#include "rdma_ch_RdmaSocketOutputStream.h" + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +static jfieldID IO_fd_fdID; + +/* + * Class: jdk_net_SocketOutputStream + * Method: init + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaSocketOutputStream_init(JNIEnv *env, jclass cls) { + IO_fd_fdID = NET_GetFileDescriptorID(env); +} + +/* + * Class: jdk_net_RdmaSocketOutputStream + * Method: rdmaSocketWrite0 + * Signature: (Ljava/io/FileDescriptor;[BII)V + */ +JNIEXPORT void JNICALL +Java_rdma_ch_RdmaSocketOutputStream_rdmaSocketWrite0(JNIEnv *env, jobject this, + jobject fdObj, + jbyteArray data, + jint off, jint len) { + char *bufP; + char BUF[MAX_BUFFER_LEN]; + int buflen; + int fd; + + if (IS_NULL(fdObj)) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } else { + fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); + if (fd == -1) { + JNU_ThrowByName(env, "java/net/SocketException", "Socket closed"); + return; + } + + } + + if (len <= MAX_BUFFER_LEN) { + bufP = BUF; + buflen = MAX_BUFFER_LEN; + } else { + buflen = min(MAX_HEAP_BUFFER_LEN, len); + bufP = (char *)malloc((size_t)buflen); + + if (bufP == NULL) { + bufP = BUF; + buflen = MAX_BUFFER_LEN; + } + } + + while(len > 0) { + int loff = 0; + int chunkLen = min(buflen, len); + int llen = chunkLen; + (*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP); + + if ((*env)->ExceptionCheck(env)) { + break; + } else { + while(llen > 0) { + int n = RDMA_Send(fd, bufP + loff, llen, 0); + if (n > 0) { + llen -= n; + loff += n; + continue; + } + if (errno == ECONNRESET) { + JNU_ThrowByName(env, "sun/net/ConnectionResetException", + "Connection reset"); + } else { + JNU_ThrowByNameWithMessageAndLastError + (env, "java/net/SocketException", "Write failed"); + } + if (bufP != BUF) { + free(bufP); + } + return; + } + len -= chunkLen; + off += chunkLen; + } + } + + if (bufP != BUF) { + free(bufP); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/rdma_util_md.c 2018-06-22 12:23:52.925672035 -0700 @@ -0,0 +1,507 @@ +/* + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 +#include +#include +#include // defines TCP_NODELAY +#include +#include +#include +#include + +#include +#include +#include + +#include "jvm.h" +#include "rdma_util_md.h" + +#include "java_net_SocketOptions.h" +#include "jdk_net_RdmaSocketOptions.h" +#include "java_net_InetAddress.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net_util.h" + +#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ + int ret; \ + threadEntry_t self; \ + fdEntry_t *fdEntry = getFdEntry(FD); \ + if (fdEntry == NULL) { \ + errno = EBADF; \ + return -1; \ + } \ + do { \ + startOp(fdEntry, &self); \ + ret = FUNC; \ + endOp(fdEntry, &self); \ + } while (ret == -1 && errno == EINTR); \ + return ret; \ +} + +typedef struct threadEntry { + pthread_t thr; /* this thread */ + struct threadEntry *next; /* next thread */ + int intr; /* interrupted */ +} threadEntry_t; + +typedef struct { + pthread_mutex_t lock; /* fd lock */ + threadEntry_t *threads; /* threads blocked on fd */ +} fdEntry_t; + +static int sigWakeup = (__SIGRTMAX - 2); + +static fdEntry_t* fdTable = NULL; + +static const int fdTableMaxSize = 0x1000; /* 4K */ + +static int fdTableLen = 0; + +static int fdLimit = 0; + +static fdEntry_t** fdOverflowTable = NULL; + +static int fdOverflowTableLen = 0; + +static const int fdOverflowTableSlabSize = 0x10000; /* 64k */ + +pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER; + +static void sig_wakeup(int sig) { +} + + +static void __attribute((constructor)) init() { + struct rlimit nbr_files; + sigset_t sigset; + struct sigaction sa; + int i = 0; + + if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) { + fprintf(stderr, "library initialization failed - " + "unable to get max # of allocated fds\n"); + abort(); + } + if (nbr_files.rlim_max != RLIM_INFINITY) { + fdLimit = nbr_files.rlim_max; + } else { + fdLimit = INT_MAX; + } + + fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize; + fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t)); + if (fdTable == NULL) { + fprintf(stderr, "library initialization failed - " + "unable to allocate file descriptor table - out of memory"); + abort(); + } else { + for (i = 0; i < fdTableLen; i ++) { + pthread_mutex_init(&fdTable[i].lock, NULL); + } + } + + if (fdLimit > fdTableMaxSize) { + fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1; + fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*)); + if (fdOverflowTable == NULL) { + fprintf(stderr, "library initialization failed - " + "unable to allocate file descriptor overflow table - out of memory"); + abort(); + } + } + + sa.sa_handler = sig_wakeup; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(sigWakeup, &sa, NULL); + + sigemptyset(&sigset); + sigaddset(&sigset, sigWakeup); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); +} + +static inline fdEntry_t *getFdEntry(int fd) +{ + fdEntry_t* result = NULL; + + if (fd < 0) { + return NULL; + } + + assert(fd < fdLimit); + + if (fd < fdTableMaxSize) { + assert(fd < fdTableLen); + result = &fdTable[fd]; + } else { + const int indexInOverflowTable = fd - fdTableMaxSize; + const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize; + const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize; + fdEntry_t* slab = NULL; + assert(rootindex < fdOverflowTableLen); + assert(slabindex < fdOverflowTableSlabSize); + pthread_mutex_lock(&fdOverflowTableLock); + if (fdOverflowTable[rootindex] == NULL) { + fdEntry_t* const newSlab = + (fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t)); + if (newSlab == NULL) { + fprintf(stderr, "Unable to allocate file descriptor overflow" + " table slab - out of memory"); + pthread_mutex_unlock(&fdOverflowTableLock); + abort(); + } else { + int i; + for (i = 0; i < fdOverflowTableSlabSize; i ++) { + pthread_mutex_init(&newSlab[i].lock, NULL); + } + fdOverflowTable[rootindex] = newSlab; + } + } + pthread_mutex_unlock(&fdOverflowTableLock); + slab = fdOverflowTable[rootindex]; + result = &slab[slabindex]; + } + + return result; + +} + +static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self) +{ + self->thr = pthread_self(); + self->intr = 0; + + pthread_mutex_lock(&(fdEntry->lock)); + { + self->next = fdEntry->threads; + fdEntry->threads = self; + } + pthread_mutex_unlock(&(fdEntry->lock)); +} + +static inline void endOp + (fdEntry_t *fdEntry, threadEntry_t *self) +{ + int orig_errno = errno; + pthread_mutex_lock(&(fdEntry->lock)); + { + threadEntry_t *curr, *prev=NULL; + curr = fdEntry->threads; + while (curr != NULL) { + if (curr == self) { + if (curr->intr) { + orig_errno = EBADF; + } + if (prev == NULL) { + fdEntry->threads = curr->next; + } else { + prev->next = curr->next; + } + break; + } + prev = curr; + curr = curr->next; + } + } + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; +} + +#define RESTARTABLE(_cmd, _result) do { \ + do { \ + _result = _cmd; \ + } while((_result == -1) && (errno == EINTR)); \ +} while(0) + +int rdma_supported() +{ + int one = 1; + int rv, s; + s = socket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +int RDMA_SocketAvailable(int s, jint *pbytes) { + int result; + RESTARTABLE(ioctl(s, FIONREAD, pbytes), result); + return (result == -1) ? 0 : 1; +} + +int +RDMA_MapSocketOption(jint cmd, int *level, int *optname) { + static struct { + jint cmd; + int level; + int optname; + } const opts[] = { + { java_net_SocketOptions_TCP_NODELAY, IPPROTO_TCP, TCP_NODELAY }, + { java_net_SocketOptions_SO_LINGER, SOL_SOCKET, SO_LINGER }, + { java_net_SocketOptions_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF }, + { java_net_SocketOptions_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF }, + { java_net_SocketOptions_SO_REUSEADDR, SOL_SOCKET, SO_REUSEADDR }, + { jdk_net_RdmaSocketOptions_SQSIZE, SOL_RDMA, RDMA_SQSIZE }, + { jdk_net_RdmaSocketOptions_RQSIZE, SOL_RDMA, RDMA_RQSIZE }, + { jdk_net_RdmaSocketOptions_INLINE, SOL_RDMA, RDMA_INLINE }, + }; + int i; + for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) { + if (cmd == opts[i].cmd) { + *level = opts[i].level; + *optname = opts[i].optname; + return 0; + } + } + + return -1; +} + +int +RDMA_GetSockOpt(int fd, int level, int opt, void *result, + int *len) +{ + int rv; + socklen_t socklen = *len; + + rv = rgetsockopt(fd, level, opt, result, &socklen); + *len = socklen; + + if (rv < 0) { + return rv; + } + + if ((level == SOL_SOCKET) && ((opt == SO_SNDBUF) + || (opt == SO_RCVBUF))) { + int n = *((int *)result); + n /= 2; + *((int *)result) = n; + } + return rv; +} + +int +RDMA_SetSockOpt(int fd, int level, int opt, const void *arg, + int len) +{ + int *bufsize; + if (level == SOL_SOCKET && opt == SO_RCVBUF) { + int *bufsize = (int *)arg; + if (*bufsize < 1024) { + *bufsize = 1024; + } + } + + return rsetsockopt(fd, level, opt, arg, len); +} + +int +RDMA_Bind(int fd, SOCKETADDRESS *sa, int len) +{ + int rv; + int arg, alen; + + if (sa->sa.sa_family == AF_INET) { + if ((ntohl(sa->sa4.sin_addr.s_addr) & 0x7f0000ff) == 0x7f0000ff) { + errno = EADDRNOTAVAIL; + return -1; + } + } + rv = rbind(fd, &sa->sa, len); + return rv; +} + +jint +RDMA_Wait(JNIEnv *env, jint fd, jint flags, jint timeout) +{ + jlong prevNanoTime = JVM_NanoTime(env, 0); + jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC; + jint read_rv; + + while (1) { + jlong newNanoTime; + struct pollfd pfd; + pfd.fd = fd; + pfd.events = 0; + if (flags & NET_WAIT_READ) + pfd.events |= POLLIN; + if (flags & NET_WAIT_WRITE) + pfd.events |= POLLOUT; + if (flags & NET_WAIT_CONNECT) + pfd.events |= POLLOUT; + + errno = 0; + read_rv = RDMA_Poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC); + + newNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= (newNanoTime - prevNanoTime); + if (nanoTimeout < NET_NSEC_PER_MSEC) { + return read_rv > 0 ? 0 : -1; + } + prevNanoTime = newNanoTime; + + if (read_rv > 0) { + break; + } + } + return (nanoTimeout / NET_NSEC_PER_MSEC); +} + +static int rdma_closefd(int fd1, int fd2) { + int rv, orig_errno; + fdEntry_t *fdEntry = getFdEntry(fd2); + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + pthread_mutex_lock(&(fdEntry->lock)); + + { + do { + if (fd1 < 0) { + rv = rclose(fd2); + } else { +// rv = dup2(fd1, fd2); + } + } while (rv == -1 && errno == EINTR); + + threadEntry_t *curr = fdEntry->threads; + while (curr != NULL) { + curr->intr = 1; + pthread_kill( curr->thr, sigWakeup ); + curr = curr->next; + } + } + + orig_errno = errno; + pthread_mutex_unlock(&(fdEntry->lock)); + errno = orig_errno; + + return rv; +} + +int RDMA_Dup2(int fd, int fd2) { + if (fd < 0) { + errno = EBADF; + return -1; + } + return rdma_closefd(fd, fd2); +} + +int RDMA_SocketClose(int fd) { + return rdma_closefd(-1, fd); +} + +int RDMA_Read(int s, void* buf, size_t len) { + BLOCKING_IO_RETURN_INT( s, rrecv(s, buf, len, 0) ); +} + +int RDMA_NonBlockingRead(int s, void* buf, size_t len) { + BLOCKING_IO_RETURN_INT( s, rrecv(s, buf, len, MSG_DONTWAIT) ); +} + +int RDMA_ReadV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, rreadv(s, vector, count) ); +} + +int RDMA_RecvFrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen) { + BLOCKING_IO_RETURN_INT( s, rrecvfrom(s, buf, len, flags, from, fromlen) ); +} + +int RDMA_Send(int s, void *msg, int len, unsigned int flags) { + BLOCKING_IO_RETURN_INT( s, rsend(s, msg, len, flags) ); +} + +int RDMA_WriteV(int s, const struct iovec * vector, int count) { + BLOCKING_IO_RETURN_INT( s, rwritev(s, vector, count) ); +} + +int NET_RSendTo(int s, const void *msg, int len, unsigned int + flags, const struct sockaddr *to, int tolen) { + BLOCKING_IO_RETURN_INT( s, rsendto(s, msg, len, flags, to, tolen) ); +} + +int RDMA_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { + BLOCKING_IO_RETURN_INT( s, raccept(s, addr, addrlen) ); +} + +int RDMA_Connect(int s, struct sockaddr *addr, int addrlen) { + BLOCKING_IO_RETURN_INT( s, rconnect(s, addr, addrlen) ); +} + +int RDMA_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { + BLOCKING_IO_RETURN_INT( ufds[0].fd, rpoll(ufds, nfds, timeout) ); +} + +int RDMA_Timeout(JNIEnv *env, int s, long timeout, jlong nanoTimeStamp) { + jlong prevNanoTime = nanoTimeStamp; + jlong nanoTimeout = (jlong)timeout * NET_NSEC_PER_MSEC; + fdEntry_t *fdEntry = getFdEntry(s); + + if (fdEntry == NULL) { + errno = EBADF; + return -1; + } + + for(;;) { + struct pollfd pfd; + int rv; + threadEntry_t self; + + pfd.fd = s; + pfd.events = POLLIN | POLLERR; + + startOp(fdEntry, &self); + rv = rpoll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC); + endOp(fdEntry, &self); + if (rv < 0 && errno == EINTR) { + jlong newNanoTime = JVM_NanoTime(env, 0); + nanoTimeout -= newNanoTime - prevNanoTime; + if (nanoTimeout < NET_NSEC_PER_MSEC) { + return 0; + } + prevNanoTime = newNanoTime; + } else { + return rv; + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/linux/native/libextnet/rdma_util_md.h 2018-06-22 12:23:53.427672021 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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. + */ + +#ifndef RDMA_UTILS_MD_H +#define RDMA_UTILS_MD_H + +#include +#include +#include +#include +#include + +/************************************************************************ + * Functions + */ +int rdma_supported(); +int RDMA_MapSocketOption(jint cmd, int *level, int *optname); +int RDMA_Bind(int fd, SOCKETADDRESS *sa, int len); +int RDMA_Timeout(JNIEnv *env, int s, long timeout, jlong nanoTimeStamp); +int RDMA_Read(int s, void* buf, size_t len); +int RDMA_NonBlockingRead(int s, void* buf, size_t len); +int RDMA_RecvFrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, socklen_t *fromlen); +int RDMA_ReadV(int s, const struct iovec * vector, int count); +int RDMA_Send(int s, void *msg, int len, unsigned int flags); +int RDMA_SendTo(int s, const void *msg, int len, unsigned int + flags, const struct sockaddr *to, int tolen); +int RDMA_Writev(int s, const struct iovec * vector, int count); +int RDMA_Connect(int s, struct sockaddr *addr, int addrlen); +int RDMA_Accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int RDMA_SocketClose(int s); +int RDMA_Dup2(int oldfd, int newfd); +int RDMA_Poll(struct pollfd *ufds, unsigned int nfds, int timeout); +int RDMA_SocketAvailable(int s, jint *pbytes); +int RDMA_SetSockOpt(int fd, int level, int opt, const void *arg, int len); +int RDMA_GetSockOpt(int fd, int level, int opt, void *result, int *len); +#endif /* RDMA_UTILS_MD_H */ --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/jdk/net/RdmaSocketOptions.java 2018-06-22 12:23:54.051672004 -0700 @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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.io.FileDescriptor; +import java.net.SocketException; +import java.net.SocketOption; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Set; +import jdk.internal.misc.JavaIOFileDescriptorAccess; +import jdk.internal.misc.SharedSecrets; +import java.lang.annotation.Native; + +/** + * Defines socket options specific to rsockets. + * These options may be platform specific. + * + * @since 11 + */ +public final class RdmaSocketOptions { + + private static class RdmaSocketOption implements SocketOption { + private final String name; + private final Class type; + RdmaSocketOption(String name, Class type) { + this.name = name; + this.type = type; + } + @Override public String name() { return name; } + @Override public Class type() { return type; } + @Override public String toString() { return name; } + } + + private RdmaSocketOptions() { } + + public static final SocketOption RDMA_SQSIZE = new + RdmaSocketOption("RDMA_SQSIZE", Integer.class); + + public static final SocketOption RDMA_RQSIZE = new + RdmaSocketOption("RDMA_RQSIZE", Integer.class); + + public static final SocketOption RDMA_INLINE = new + RdmaSocketOption("RDMA_INLINE", Integer.class); + + @Native public static final int SQSIZE = 0x3001; + + @Native public static final int RQSIZE = 0x3002; + + @Native public static final int INLINE = 0x3003; + + private static final PlatformRdmaSocketOptions platformRdmaSocketOptions = + PlatformRdmaSocketOptions.get(); + + private static final boolean rdmaSocketSupported = + platformRdmaSocketOptions.rdmaSocketSupported(); + + private static final Set> rdmaOptions = options(); + + static Set> options() { + if (rdmaSocketSupported) + return Set.of(RDMA_SQSIZE, RDMA_RQSIZE, RDMA_INLINE); + else + return Collections.>emptySet(); + } + + static { + sun.net.ext.RdmaSocketOptions.register( + new sun.net.ext.RdmaSocketOptions(rdmaOptions) { + + @Override + public void setOption(FileDescriptor fd, + SocketOption option, + Object value) + throws SocketException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetworkPermission("setOption." + option.name())); + + if (fd == null || !fd.valid()) + throw new SocketException("socket closed"); + + if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE) + || (option == RDMA_INLINE)) { + assert rdmaSocketSupported; + + int val; + int opt; + if (option == RDMA_SQSIZE) { + if (value == null || (!(value instanceof Integer))) + throw new SocketException("Bad parameter for RDMA_SQSIZE"); + val = ((Integer) value).intValue(); + opt = SQSIZE; + if (val < 0) + throw new IllegalArgumentException("send queue size < 0"); + } else if (option == RDMA_RQSIZE) { + if (value == null || (!(value instanceof Integer))) + throw new SocketException("Bad parameter for RDMA_RQSIZE"); + val = ((Integer) value).intValue(); + opt = RQSIZE; + if (val < 0) + throw new IllegalArgumentException("receive queue size < 0"); + } else if (option == RDMA_INLINE) { + if (value == null || (!(value instanceof Integer))) + throw new SocketException("Bad parameter for RDMA_INLINE"); + val = ((Integer) value).intValue(); + opt = INLINE; + if (val < 0) + throw new IllegalArgumentException("inline size < 0"); + } else { + throw new SocketException("unrecognized RDMA socket option: " + option); + } + + setSockOpt(fd, opt, val); + + } else { + throw new InternalError("Unexpected option " + option); + } + } + + @Override + public Object getOption(FileDescriptor fd, + SocketOption option) + throws SocketException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetworkPermission("getOption." + option.name())); + + if (fd == null || !fd.valid()) + throw new SocketException("socket closed"); + + if ((option == RDMA_SQSIZE) || (option == RDMA_RQSIZE) + || (option == RDMA_INLINE)) { + assert rdmaSocketSupported; + int opt; + if (option == RDMA_SQSIZE) { + opt = SQSIZE; + } else if (option == RDMA_RQSIZE) { + opt = RQSIZE; + } else { + opt = INLINE; + } + return getSockOpt(fd, opt); + } else { + throw new InternalError("Unexpected option " + option); + } + } + }); + } + + private static final JavaIOFileDescriptorAccess fdAccess = + SharedSecrets.getJavaIOFileDescriptorAccess(); + + private static void setSockOpt(FileDescriptor fd, int opt, int val) + throws SocketException + { + platformRdmaSocketOptions.setSockOpt(fdAccess.get(fd), opt, val); + } + + private static int getSockOpt(FileDescriptor fd, int opt) + throws SocketException + { + return platformRdmaSocketOptions.getSockOpt(fdAccess.get(fd), opt); + } + + static class PlatformRdmaSocketOptions { + + protected PlatformRdmaSocketOptions() {} + + @SuppressWarnings("unchecked") + private static PlatformRdmaSocketOptions newInstance(String cn) { + Class c; + try { + c = (Class)Class.forName(cn); + return c.getConstructor(new Class[] { }).newInstance(); + } catch (ReflectiveOperationException x) { + throw new AssertionError(x); + } + } + + private static PlatformRdmaSocketOptions create() { + String osname = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("os.name"); + } + }); + if ("Linux".equals(osname)) + return newInstance("jdk.net.LinuxRdmaSocketOptions"); + return new PlatformRdmaSocketOptions(); + } + + private static final PlatformRdmaSocketOptions instance = create(); + + static PlatformRdmaSocketOptions get() { + return instance; + } + + void setSockOpt(int fd, int opt, int value) + throws SocketException + { + throw new UnsupportedOperationException("unsupported socket option"); + } + + int getSockOpt(int fd, int opt) throws SocketException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + boolean rdmaSocketSupported() { + return false; + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaInetAddressContainer.java 2018-06-22 12:23:54.651671988 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013, 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 rdma.ch; + +import java.net.InetAddress; +class RdmaInetAddressContainer { + InetAddress addr; +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaNet.java 2018-06-22 12:23:55.160671975 -0700 @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.*; +import java.net.*; +import java.nio.channels.*; +import java.util.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.net.ext.RdmaSocketOptions; +import sun.nio.ch.Net; +import sun.nio.ch.IOUtil; + +public class RdmaNet extends Net { + protected RdmaNet() { } + + static final ProtocolFamily UNSPEC = new ProtocolFamily() { + public String name() { + return "UNSPEC"; + } + }; + + static boolean isReusePortAvailable() { + return false; + } + + private static volatile boolean checkedRdma; + private static volatile boolean isRdmaAvailable; + + static boolean isRdmaAvailable() { + if (!checkedRdma) { + isRdmaAvailable = isRdmaAvailable0(); + checkedRdma = true; + } + return isRdmaAvailable; + } + + private static native boolean isRdmaAvailable0(); + + /** + * Returns true if exclusive binding is on + */ + static boolean useExclusiveBind() { + return false; + } + + // -- Socket options + + static final RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + + static void setSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name, Object value) + throws IOException + { + if (value == null) + throw new IllegalArgumentException("Invalid option value"); + + Class type = name.type(); + + if (rdmaOptions.isOptionSupported(name)) { + rdmaOptions.setOption(fd, name, value); + return; + } + + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + if (name == StandardSocketOptions.SO_RCVBUF || + name == StandardSocketOptions.SO_SNDBUF) + { + int i = ((Integer)value).intValue(); + if (i < 0) + throw new IllegalArgumentException("Invalid send/receive buffer size"); + } + if (name == StandardSocketOptions.SO_LINGER) { + int i = ((Integer)value).intValue(); + if (i < 0) + value = Integer.valueOf(-1); + if (i > 65535) + value = Integer.valueOf(65535); + } + RdmaOptionKey key = RdmaSocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + int arg; + int maxValue = 1024 * 1024 * 1024 - 1; + if (type == Integer.class) { + arg = ((Integer)value).intValue(); + if (arg > maxValue) + arg = maxValue; + } else { + boolean b = ((Boolean)value).booleanValue(); + arg = (b) ? 1 : 0; + } + + boolean mayNeedConversion = (family == UNSPEC); + boolean isIPv6 = (family == StandardProtocolFamily.INET6); + setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg, isIPv6); + } + + static Object getSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name) + throws IOException + { + Class type = name.type(); + + if (rdmaOptions.isOptionSupported(name)) { + return rdmaOptions.getOption(fd, name); + } + + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + RdmaOptionKey key = RdmaSocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + boolean mayNeedConversion = (family == UNSPEC); + int value = getIntOption0(fd, mayNeedConversion, key.level(), key.name()); + + if (type == Integer.class) { + return Integer.valueOf(value); + } else { + return (value == 0) ? Boolean.FALSE : Boolean.TRUE; + } + } + + // -- Socket operations -- + + static FileDescriptor socket(boolean stream) throws IOException { + return socket(UNSPEC, stream); + } + + static FileDescriptor socket(ProtocolFamily family, boolean stream) + throws IOException { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return IOUtil.newFD(socket0(preferIPv6, stream, false, false)); + } + + static FileDescriptor serverSocket(boolean stream) { + return IOUtil.newFD(socket0(isIPv6Available(), stream, true, false)); + } + + private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse, + boolean fastLoopback); + + public static void bind(FileDescriptor fd, InetAddress addr, int port) + throws IOException + { + bind(UNSPEC, fd, addr, port); + } + + static void bind(ProtocolFamily family, FileDescriptor fd, + InetAddress addr, int port) throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + bind0(fd, preferIPv6, false, addr, port); + } + + private static native void bind0(FileDescriptor fd, boolean preferIPv6, + boolean useExclBind, InetAddress addr, + int port) + throws IOException; + + static native void listen(FileDescriptor fd, int backlog) throws IOException; + + static int connect(FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + return connect(UNSPEC, fd, remote, remotePort); + } + + static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return connect0(preferIPv6, fd, remote, remotePort); + } + + public static InetSocketAddress localAddress(FileDescriptor fd) + throws IOException + { + return new InetSocketAddress(localInetAddress(fd), localPort(fd)); + } + + private static native int connect0(boolean preferIPv6, + FileDescriptor fd, + InetAddress remote, + int remotePort) + throws IOException; + + static native void shutdown(FileDescriptor fd, int how) throws IOException; + + private static native int localPort(FileDescriptor fd) + throws IOException; + + private static native InetAddress localInetAddress(FileDescriptor fd) + throws IOException; + + private static native int getIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt) + throws IOException; + + private static native void setIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt, int arg, boolean isIPv6) + throws IOException; + + static native int poll(FileDescriptor fd, int events, long timeout) + throws IOException; + + public static native void configureBlocking(FileDescriptor fd, boolean blocking); + + private static native void initIDs(); + + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<>() { + public Void run() { + System.loadLibrary("extnet"); + return null; + } + }); + IOUtil.load(); + initIDs(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaOptionKey.java 2018-06-22 12:23:55.672671961 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +/** + * Represents the level/name of a RDMA socket option + */ + +class RdmaOptionKey { + private int level; + private int name; + + RdmaOptionKey(int level, int name) { + this.level = level; + this.name = name; + } + + int level() { + return level; + } + + int name() { + return name; + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaPollSelectorImpl.java 2018-06-22 12:23:56.181671947 -0700 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.IOException; +import java.nio.channels.spi.SelectorProvider; +import sun.nio.ch.PollSelectorImpl; +import sun.nio.ch.IOUtil; + +import jdk.internal.misc.Unsafe; + +/** + * Selector implementation based on poll + */ + +public class RdmaPollSelectorImpl extends PollSelectorImpl { + + protected RdmaPollSelectorImpl(SelectorProvider sp) throws IOException { + super(sp); + } + + protected int poll(long pollAddress, int numfds, int timeout) { + return poll0(pollAddress, numfds, timeout); + } + + private static native int poll0(long pollAddress, int numfds, int timeout); + + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<>() { + public Void run() { + System.loadLibrary("extnet"); + return null; + } + }); + IOUtil.load(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaPollSelectorProvider.java 2018-06-22 12:23:56.746671932 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.IOException; +import java.nio.channels.*; +import java.nio.channels.spi.*; +import sun.nio.ch.SelectorProviderImpl; +import java.security.AccessController; +import java.security.PrivilegedAction; + +public class RdmaPollSelectorProvider + extends SelectorProviderImpl +{ + private static final Object lock = new Object(); + private static SelectorProvider provider = null; + + public static SelectorProvider provider() { + synchronized (lock) { + if (provider != null) + return provider; + return AccessController.doPrivileged( + new PrivilegedAction<>() { + public SelectorProvider run() { + provider = new RdmaPollSelectorProvider(); + return provider; + } + }); + } + } + + public AbstractSelector openSelector() throws IOException { + return new RdmaPollSelectorImpl(this); + } + + public SocketChannel openSocketChannel() throws IOException { + return new RdmaSocketChannelImpl(this); + } + + public ServerSocketChannel openServerSocketChannel() throws IOException { + return new RdmaServerSocketChannelImpl(this); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaServerSocketAdaptor.java 2018-06-22 12:23:57.263671918 -0700 @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.StandardSocketOptions; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +class RdmaServerSocketAdaptor + extends ServerSocket +{ + private final RdmaServerSocketChannelImpl ssc; + + private volatile int timeout; + + public static ServerSocket create(RdmaServerSocketChannelImpl ssc) { + try { + return new RdmaServerSocketAdaptor(ssc); + } catch (IOException x) { + throw new Error(x); + } + } + + private RdmaServerSocketAdaptor(RdmaServerSocketChannelImpl ssc) throws IOException { + this.ssc = ssc; + } + + public void bind(SocketAddress local) throws IOException { + bind(local, 50); + } + + public void bind(SocketAddress local, int backlog) throws IOException { + if (local == null) + local = new InetSocketAddress(0); + try { + ssc.bind(local, backlog); + } catch (Exception x) { + RdmaNet.translateException(x); + } + } + + public InetAddress getInetAddress() { + InetSocketAddress local = ssc.localAddress(); + if (local == null) { + return null; + } else { + return RdmaNet.getRevealedLocalAddress(local).getAddress(); + } + } + + public int getLocalPort() { + InetSocketAddress local = ssc.localAddress(); + if (local == null) { + return -1; + } else { + return local.getPort(); + } + } + + public Socket accept() throws IOException { + synchronized (ssc.blockingLock()) { + try { + if (!ssc.isBound()) + throw new NotYetBoundException(); + + long to = this.timeout; + if (to == 0) { + // for compatibility reasons: accept connection if available + // when configured non-blocking + SocketChannel sc = ssc.accept(); + if (sc == null && !ssc.isBlocking()) + throw new IllegalBlockingModeException(); + return sc.socket(); + } + + if (!ssc.isBlocking()) + throw new IllegalBlockingModeException(); + for (;;) { + long st = System.currentTimeMillis(); + if (ssc.pollAccept(to)) + return ssc.accept().socket(); + to -= System.currentTimeMillis() - st; + if (to <= 0) + throw new SocketTimeoutException(); + } + + } catch (Exception x) { + RdmaNet.translateException(x); + assert false; + return null; // Never happens + } + } + } + + public void close() throws IOException { + ssc.close(); + } + + public ServerSocketChannel getChannel() { + return ssc; + } + + public boolean isBound() { + return ssc.isBound(); + } + + public boolean isClosed() { + return !ssc.isOpen(); + } + + public void setSoTimeout(int timeout) throws SocketException { + this.timeout = timeout; + } + + public int getSoTimeout() throws SocketException { + return timeout; + } + + public void setReuseAddress(boolean on) throws SocketException { + try { + ssc.setOption(StandardSocketOptions.SO_REUSEADDR, on); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + } + } + + public boolean getReuseAddress() throws SocketException { + try { + return ssc.getOption(StandardSocketOptions.SO_REUSEADDR).booleanValue(); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + return false; // Never happens + } + } + + public String toString() { + if (!isBound()) + return "RdmaServerSocket[unbound]"; + return "RdmaServerSocket[addr=" + getInetAddress() + + ",localport=" + getLocalPort() + "]"; + } + + public void setReceiveBufferSize(int size) throws SocketException { + // size 0 valid for ServerSocketChannel, invalid for ServerSocket + if (size <= 0) + throw new IllegalArgumentException("size cannot be 0 or negative"); + try { + ssc.setOption(StandardSocketOptions.SO_RCVBUF, size); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + } + } + + public int getReceiveBufferSize() throws SocketException { + try { + return ssc.getOption(StandardSocketOptions.SO_RCVBUF).intValue(); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + return -1; // Never happens + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaServerSocketChannelImpl.java 2018-06-22 12:23:57.806671903 -0700 @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; +import sun.nio.ch.IOStatus; +import sun.nio.ch.IOUtil; +import sun.nio.ch.NativeThread; +import sun.nio.ch.SelChImpl; +import sun.nio.ch.SelectionKeyImpl; +import sun.net.ext.RdmaSocketOptions; + +public class RdmaServerSocketChannelImpl + extends ServerSocketChannel + implements SelChImpl +{ + private static RdmaSocketDispatcher nd; + + private final FileDescriptor fd; + private final int fdVal; + + private final ReentrantLock acceptLock = new ReentrantLock(); + + private final Object stateLock = new Object(); + + private static final int ST_INUSE = 0; + private static final int ST_CLOSING = 1; + private static final int ST_KILLPENDING = 2; + private static final int ST_KILLED = 3; + private int state; + + private long thread; + + private InetSocketAddress localAddress; + + private boolean isReuseAddress; + + private ServerSocket socket; + + RdmaServerSocketChannelImpl(SelectorProvider sp) throws IOException { + super(sp); + this.fd = RdmaNet.serverSocket(true); + this.fdVal = IOUtil.fdVal(fd); + } + + RdmaServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) + throws IOException + { + super(sp); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + if (bound) { + synchronized (stateLock) { + localAddress = RdmaNet.localAddress(fd); + } + } + } + + private void ensureOpen() throws ClosedChannelException { + if (!isOpen()) + throw new ClosedChannelException(); + } + + @Override + public ServerSocket socket() { + synchronized (stateLock) { + if (socket == null) + socket = RdmaServerSocketAdaptor.create(this); + return socket; + } + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + ensureOpen(); + return (localAddress == null) + ? null + : RdmaNet.getRevealedLocalAddress(localAddress); + } + } + + @Override + public ServerSocketChannel setOption(SocketOption name, T value) + throws IOException + { + Objects.requireNonNull(name); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOptions.SO_REUSEADDR && + RdmaNet.useExclusiveBind()) { + isReuseAddress = (Boolean)value; + return this; + } + + if (isBound() && (name == StandardSocketOptions.SO_REUSEADDR || + name == StandardSocketOptions.SO_RCVBUF)) + throw new UnsupportedOperationException( + "RDMA server socket channel cannot set the socket option " + + name.toString() + " after bind."); + + RdmaNet.setSocketOption(fd, RdmaNet.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + Objects.requireNonNull(name); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + ensureOpen(); + if (name == StandardSocketOptions.SO_REUSEADDR && RdmaNet.useExclusiveBind()) { + return (T)Boolean.valueOf(isReuseAddress); + } + return (T) RdmaNet.getSocketOption(fd, RdmaNet.UNSPEC, name); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet<>(2); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); + if (RdmaNet.isRdmaAvailable()) { + RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + set.addAll(rdmaOptions.options()); + } + return Collections.unmodifiableSet(set); + } + } + + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + @Override + public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException { + synchronized (stateLock) { + ensureOpen(); + if (localAddress != null) + throw new AlreadyBoundException(); + InetSocketAddress isa = (local == null) + ? new InetSocketAddress(0) + : RdmaNet.checkAddress(local); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkListen(isa.getPort()); + RdmaNet.bind(fd, isa.getAddress(), isa.getPort()); + RdmaNet.listen(fd, backlog < 1 ? 50 : backlog); + localAddress = RdmaNet.localAddress(fd); + } + return this; + } + + private void begin(boolean blocking) throws ClosedChannelException { + if (blocking) + begin(); + synchronized (stateLock) { + ensureOpen(); + if (localAddress == null) + throw new NotYetBoundException(); + if (blocking) + thread = NativeThread.current(); + } + } + + private void end(boolean blocking, boolean completed) + throws AsynchronousCloseException + { + if (blocking) { + synchronized (stateLock) { + thread = 0; + if (state == ST_CLOSING) { + stateLock.notifyAll(); + } + } + end(completed); + } + } + + @Override + public SocketChannel accept() throws IOException { + acceptLock.lock(); + try { + int n = 0; + FileDescriptor newfd = new FileDescriptor(); + InetSocketAddress[] isaa = new InetSocketAddress[1]; + + boolean blocking = isBlocking(); + try { + begin(blocking); + do { + n = accept(this.fd, newfd, isaa); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } finally { + end(blocking, n > 0); + assert IOStatus.check(n); + } + + if (n < 1) + return null; + + // newly accepted socket is initially in blocking mode + RdmaNet.configureBlocking(newfd, true); + + InetSocketAddress isa = isaa[0]; + SocketChannel sc = new RdmaSocketChannelImpl(provider(), newfd, isa); + + // check permitted to accept connections from the remote address + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort()); + } catch (SecurityException x) { + sc.close(); + throw x; + } + } + return sc; + + } finally { + acceptLock.unlock(); + } + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + acceptLock.lock(); + try { + synchronized (stateLock) { + ensureOpen(); + RdmaNet.configureBlocking(fd, block); + } + } finally { + acceptLock.unlock(); + } + } + + @Override + protected void implCloseSelectableChannel() throws IOException { + assert !isOpen(); + + boolean interrupted = false; + boolean blocking; + + // set state to ST_CLOSING + synchronized (stateLock) { + assert state < ST_CLOSING; + state = ST_CLOSING; + blocking = isBlocking(); + } + + // wait for any outstanding accept to complete + if (blocking) { + synchronized (stateLock) { + assert state == ST_CLOSING; + long th = thread; + if (th != 0) { + nd.preClose(fd); + NativeThread.signal(th); + + // wait for accept operation to end + while (thread != 0) { + try { + stateLock.wait(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } + } + } else { + // non-blocking mode: wait for accept to complete + acceptLock.lock(); + acceptLock.unlock(); + } + + // set state to ST_KILLPENDING + synchronized (stateLock) { + assert state == ST_CLOSING; + state = ST_KILLPENDING; + } + + // close socket if not registered with Selector + if (!isRegistered()) + kill(); + + // restore interrupt status + if (interrupted) + Thread.currentThread().interrupt(); + } + + @Override + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ST_KILLPENDING) { + state = ST_KILLED; + nd.close(fd); + } + } + } + + boolean isBound() { + synchronized (stateLock) { + return localAddress != null; + } + } + + InetSocketAddress localAddress() { + synchronized (stateLock) { + return localAddress; + } + } + + /** + * Poll this channel's socket for a new connection up to the given timeout. + * @return {@code true} if there is a connection to accept + */ + boolean pollAccept(long timeout) throws IOException { + assert Thread.holdsLock(blockingLock()) && isBlocking(); + acceptLock.lock(); + try { + boolean polled = false; + try { + begin(true); + int events = RdmaNet.poll(fd, RdmaNet.POLLIN, timeout); + polled = (events != 0); + } finally { + end(true, polled); + } + return polled; + } finally { + acceptLock.unlock(); + } + } + + public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) { + int intOps = ski.nioInterestOps(); + int oldOps = ski.nioReadyOps(); + int newOps = initialOps; + + if ((ops & RdmaNet.POLLNVAL) != 0) { + return false; + } + + if ((ops & (RdmaNet.POLLERR | RdmaNet.POLLHUP)) != 0) { + newOps = intOps; + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + if (((ops & RdmaNet.POLLIN) != 0) && + ((intOps & SelectionKey.OP_ACCEPT) != 0)) + newOps |= SelectionKey.OP_ACCEPT; + + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, ski.nioReadyOps(), ski); + } + + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, 0, ski); + } + + public int translateInterestOps(int ops) { + int newOps = 0; + if ((ops & SelectionKey.OP_ACCEPT) != 0) + newOps |= RdmaNet.POLLIN; + return newOps; + } + + public FileDescriptor getFD() { + return fd; + } + + public int getFDVal() { + return fdVal; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getName()); + sb.append('['); + if (!isOpen()) { + sb.append("closed"); + } else { + synchronized (stateLock) { + InetSocketAddress addr = localAddress; + if (addr == null) { + sb.append("unbound"); + } else { + sb.append(RdmaNet.getRevealedLocalAddressAsString(addr)); + } + } + } + sb.append(']'); + return sb.toString(); + } + + private int accept(FileDescriptor ssfd, + FileDescriptor newfd, + InetSocketAddress[] isaa) + throws IOException + { + return accept0(ssfd, newfd, isaa); + } + + // -- Native methods -- + + private native int accept0(FileDescriptor ssfd, + FileDescriptor newfd, + InetSocketAddress[] isaa) + throws IOException; + + private static native void initIDs(); + + static { + IOUtil.load(); + initIDs(); + nd = new RdmaSocketDispatcher(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketAdaptor.java 2018-06-22 12:23:58.385671888 -0700 @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketOption; +import java.net.SocketTimeoutException; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.SocketChannel; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import sun.nio.ch.ChannelInputStream; +import sun.nio.ch.ExtendedSocketOption; + +import static java.util.concurrent.TimeUnit.*; + +class RdmaSocketAdaptor + extends Socket +{ + // The channel being adapted + private final RdmaSocketChannelImpl sc; + + // Timeout "option" value for reads + private volatile int timeout; + + private RdmaSocketAdaptor(RdmaSocketChannelImpl sc) throws SocketException { + super((RdmaSocketImpl) null); + this.sc = sc; + } + + public static Socket create(RdmaSocketChannelImpl sc) { + try { + return new RdmaSocketAdaptor(sc); + } catch (SocketException e) { + throw new InternalError("Should not reach here"); + } + } + + public SocketChannel getChannel() { + return sc; + } + + // Override this method just to protect against changes in the superclass + // + public void connect(SocketAddress remote) throws IOException { + connect(remote, 0); + } + + public void connect(SocketAddress remote, int timeout) throws IOException { + if (remote == null) + throw new IllegalArgumentException("connect: The address can't be null"); + if (timeout < 0) + throw new IllegalArgumentException("connect: timeout can't be negative"); + + synchronized (sc.blockingLock()) { + if (!sc.isBlocking()) + throw new IllegalBlockingModeException(); + + try { + if (timeout == 0) { + sc.connect(remote); + return; + } + + sc.configureBlocking(false); + try { + if (sc.connect(remote)) + return; + } finally { + try { + sc.configureBlocking(true); + } catch (ClosedChannelException e) { } + } + + long timeoutNanos = NANOSECONDS.convert(timeout, MILLISECONDS); + long to = timeout; + for (;;) { + long startTime = System.nanoTime(); + if (sc.pollConnected(to)) { + boolean connected = sc.finishConnect(); + assert connected; + break; + } + timeoutNanos -= System.nanoTime() - startTime; + if (timeoutNanos <= 0) { + try { + sc.close(); + } catch (IOException x) { } + throw new SocketTimeoutException(); + } + to = MILLISECONDS.convert(timeoutNanos, NANOSECONDS); + } + + } catch (Exception x) { + RdmaNet.translateException(x, true); + } + } + + } + + public void bind(SocketAddress local) throws IOException { + try { + sc.bind(local); + } catch (Exception x) { + RdmaNet.translateException(x); + } + } + + public InetAddress getInetAddress() { + InetSocketAddress remote = sc.remoteAddress(); + if (remote == null) { + return null; + } else { + return remote.getAddress(); + } + } + + public InetAddress getLocalAddress() { + if (sc.isOpen()) { + InetSocketAddress local = sc.localAddress(); + if (local != null) { + return RdmaNet.getRevealedLocalAddress(local).getAddress(); + } + } + return new InetSocketAddress(0).getAddress(); + } + + public int getPort() { + InetSocketAddress remote = sc.remoteAddress(); + if (remote == null) { + return 0; + } else { + return remote.getPort(); + } + } + + public int getLocalPort() { + InetSocketAddress local = sc.localAddress(); + if (local == null) { + return -1; + } else { + return local.getPort(); + } + } + + private class SocketInputStream + extends ChannelInputStream + { + private SocketInputStream() { + super(sc); + } + + protected int read(ByteBuffer bb) + throws IOException + { + synchronized (sc.blockingLock()) { + if (!sc.isBlocking()) + throw new IllegalBlockingModeException(); + + // no timeout + long to = RdmaSocketAdaptor.this.timeout; + if (to == 0) + return sc.read(bb); + + // timed read + long timeoutNanos = NANOSECONDS.convert(to, MILLISECONDS); + for (;;) { + long startTime = System.nanoTime(); + if (sc.pollRead(to)) { + return sc.read(bb); + } + timeoutNanos -= System.nanoTime() - startTime; + if (timeoutNanos <= 0) + throw new SocketTimeoutException(); + to = MILLISECONDS.convert(timeoutNanos, NANOSECONDS); + } + } + } + } + + private InputStream socketInputStream = null; + + public InputStream getInputStream() throws IOException { + if (!sc.isOpen()) + throw new SocketException("Socket is closed"); + if (!sc.isConnected()) + throw new SocketException("Socket is not connected"); + if (!sc.isInputOpen()) + throw new SocketException("Socket input is shutdown"); + if (socketInputStream == null) { + try { + socketInputStream = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public InputStream run() throws IOException { + return new SocketInputStream(); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException)e.getException(); + } + } + return socketInputStream; + } + + public OutputStream getOutputStream() throws IOException { + if (!sc.isOpen()) + throw new SocketException("Socket is closed"); + if (!sc.isConnected()) + throw new SocketException("Socket is not connected"); + if (!sc.isOutputOpen()) + throw new SocketException("Socket output is shutdown"); + OutputStream os = null; + try { + os = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public OutputStream run() throws IOException { + return Channels.newOutputStream(sc); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException)e.getException(); + } + return os; + } + + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).booleanValue(); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).intValue(); + } catch (IOException x) { + RdmaNet.translateToSocketException(x); + return -1; // keep compiler happy + } + } + + public void setTcpNoDelay(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.TCP_NODELAY, on); + } + + public boolean getTcpNoDelay() throws SocketException { + return getBooleanOption(StandardSocketOptions.TCP_NODELAY); + } + + + public void setSoLinger(boolean on, int linger) throws SocketException { + if (!on) + linger = -1; + setIntOption(StandardSocketOptions.SO_LINGER, linger); + } + + public int getSoLinger() throws SocketException { + return getIntOption(StandardSocketOptions.SO_LINGER); + } + + public void sendUrgentData(int data) throws IOException { + int n = sc.sendOutOfBandData((byte) data); + if (n == 0) + throw new IOException("Socket buffer full"); + } + + public void setSoTimeout(int timeout) throws SocketException { + if (timeout < 0) + throw new IllegalArgumentException("timeout can't be negative"); + this.timeout = timeout; + } + + public int getSoTimeout() throws SocketException { + return timeout; + } + + public void setSendBufferSize(int size) throws SocketException { + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOptions.SO_SNDBUF, size); + } + + public int getSendBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_SNDBUF); + } + + public void setReceiveBufferSize(int size) throws SocketException { + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOptions.SO_RCVBUF, size); + } + + public int getReceiveBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_RCVBUF); + } + + public void setReuseAddress(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); + } + + public boolean getReuseAddress() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); + } + + public void close() throws IOException { + sc.close(); + } + + public void shutdownInput() throws IOException { + try { + sc.shutdownInput(); + } catch (Exception x) { + RdmaNet.translateException(x); + } + } + + public void shutdownOutput() throws IOException { + try { + sc.shutdownOutput(); + } catch (Exception x) { + RdmaNet.translateException(x); + } + } + + public String toString() { + if (sc.isConnected()) + return "RdmaSocket[addr=" + getInetAddress() + + ",port=" + getPort() + + ",localport=" + getLocalPort() + "]"; + return "RdmaSocket[unconnected]"; + } + + public boolean isConnected() { + return sc.isConnected(); + } + + public boolean isBound() { + return sc.localAddress() != null; + } + + public boolean isClosed() { + return !sc.isOpen(); + } + + public boolean isInputShutdown() { + return !sc.isInputOpen(); + } + + public boolean isOutputShutdown() { + return !sc.isOutputOpen(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketChannelImpl.java 2018-06-22 12:23:58.902671874 -0700 @@ -0,0 +1,983 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AlreadyConnectedException; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ConnectionPendingException; +import java.nio.channels.NoConnectionPendingException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; +import sun.net.ext.RdmaSocketOptions; +import sun.nio.ch.ExtendedSocketOption; +import sun.nio.ch.IOStatus; +import sun.nio.ch.IOUtil; +import sun.nio.ch.Net; +import sun.nio.ch.NativeThread; +import sun.nio.ch.SelChImpl; +import sun.nio.ch.SelectionKeyImpl; + +public class RdmaSocketChannelImpl + extends SocketChannel + implements SelChImpl +{ + private static RdmaSocketDispatcher nd; + private final FileDescriptor fd; + private final int fdVal; + + private final ReentrantLock readLock = new ReentrantLock(); + private final ReentrantLock writeLock = new ReentrantLock(); + + private final Object stateLock = new Object(); + + private volatile boolean isInputClosed; + private volatile boolean isOutputClosed; + + private boolean isReuseAddress; + + private static final int ST_UNCONNECTED = 0; + private static final int ST_CONNECTIONPENDING = 1; + private static final int ST_CONNECTED = 2; + private static final int ST_CLOSING = 3; + private static final int ST_KILLPENDING = 4; + private static final int ST_KILLED = 5; + private volatile int state; // need stateLock to change + + private long readerThread; + private long writerThread; + + private InetSocketAddress localAddress; + private InetSocketAddress remoteAddress; + + private Socket socket; + + protected RdmaSocketChannelImpl(SelectorProvider sp) throws IOException { + super(sp); + this.fd = RdmaNet.socket(true); + this.fdVal = IOUtil.fdVal(fd); + } + + protected RdmaSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound) + throws IOException + { + super(sp); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + if (bound) { + synchronized (stateLock) { + this.localAddress = RdmaNet.localAddress(fd); + } + } + } + + RdmaSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa) + throws IOException + { + super(sp); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + synchronized (stateLock) { + this.localAddress = RdmaNet.localAddress(fd); + this.remoteAddress = isa; + this.state = ST_CONNECTED; + } + } + + private void ensureOpen() throws ClosedChannelException { + if (!isOpen()) + throw new ClosedChannelException(); + } + + private void ensureOpenAndConnected() throws ClosedChannelException { + int state = this.state; + if (state < ST_CONNECTED) { + throw new NotYetConnectedException(); + } else if (state > ST_CONNECTED) { + throw new ClosedChannelException(); + } + } + + @Override + public Socket socket() { + synchronized (stateLock) { + if (socket == null) + socket = RdmaSocketAdaptor.create(this); + return socket; + } + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + ensureOpen(); + return RdmaNet.getRevealedLocalAddress(localAddress); + } + } + + @Override + public SocketAddress getRemoteAddress() throws IOException { + synchronized (stateLock) { + ensureOpen(); + return remoteAddress; + } + } + + @Override + public SocketChannel setOption(SocketOption name, T value) + throws IOException + { + Objects.requireNonNull(name); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOptions.SO_REUSEADDR && + RdmaNet.useExclusiveBind()) { + isReuseAddress = (Boolean)value; + return this; + } + + if (isConnected() && (name == StandardSocketOptions.SO_REUSEADDR || + name == StandardSocketOptions.SO_SNDBUF || + name == StandardSocketOptions.SO_RCVBUF)) + throw new UnsupportedOperationException( + "RDMA socket channel cannot set the socket option " + + name.toString() + " after connect."); + + RdmaNet.setSocketOption(fd, RdmaNet.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + Objects.requireNonNull(name); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOptions.SO_REUSEADDR + && RdmaNet.useExclusiveBind()) { + return (T)Boolean.valueOf(isReuseAddress); + } + + return (T) RdmaNet.getSocketOption(fd, RdmaNet.UNSPEC, name); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet<>(); + set.add(StandardSocketOptions.SO_SNDBUF); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); + set.add(StandardSocketOptions.SO_LINGER); + set.add(StandardSocketOptions.TCP_NODELAY); + RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + set.addAll(rdmaOptions.options()); + return Collections.unmodifiableSet(set); + } + } + + public Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + private void beginRead(boolean blocking) throws ClosedChannelException { + if (blocking) { + // set hook for Thread.interrupt + begin(); + + synchronized (stateLock) { + ensureOpenAndConnected(); + // record thread so it can be signalled if needed + readerThread = NativeThread.current(); + } + } else { + ensureOpenAndConnected(); + } + } + private void endRead(boolean blocking, boolean completed) + throws AsynchronousCloseException + { + if (blocking) { + synchronized (stateLock) { + readerThread = 0; + // notify any thread waiting in implCloseSelectableChannel + if (state == ST_CLOSING) { + stateLock.notifyAll(); + } + } + // remove hook for Thread.interrupt + end(completed); + } + } + + @Override + public int read(ByteBuffer buf) throws IOException { + Objects.requireNonNull(buf); + + readLock.lock(); + try { + boolean blocking = isBlocking(); + int n = 0; + try { + beginRead(blocking); + + // check if input is shutdown + if (isInputClosed) + return IOStatus.EOF; + + if (blocking) { + do { + n = IOUtil.read(fd, buf, -1, nd); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = IOUtil.read(fd, buf, -1, nd); + } + } finally { + endRead(blocking, n > 0); + if (n <= 0 && isInputClosed) + return IOStatus.EOF; + } + return IOStatus.normalize(n); + } finally { + readLock.unlock(); + } + } + + @Override + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + Objects.checkFromIndexSize(offset, length, dsts.length); + + readLock.lock(); + try { + boolean blocking = isBlocking(); + long n = 0; + try { + beginRead(blocking); + + // check if input is shutdown + if (isInputClosed) + return IOStatus.EOF; + + if (blocking) { + do { + n = IOUtil.read(fd, dsts, offset, length, nd); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = IOUtil.read(fd, dsts, offset, length, nd); + } + } finally { + endRead(blocking, n > 0); + if (n <= 0 && isInputClosed) + return IOStatus.EOF; + } + return IOStatus.normalize(n); + } finally { + readLock.unlock(); + } + } + + private void beginWrite(boolean blocking) throws ClosedChannelException { + if (blocking) { + // set hook for Thread.interrupt + begin(); + + synchronized (stateLock) { + ensureOpenAndConnected(); + if (isOutputClosed) + throw new ClosedChannelException(); + // record thread so it can be signalled if needed + writerThread = NativeThread.current(); + } + } else { + ensureOpenAndConnected(); + } + } + + private void endWrite(boolean blocking, boolean completed) + throws AsynchronousCloseException + { + if (blocking) { + synchronized (stateLock) { + writerThread = 0; + // notify any thread waiting in implCloseSelectableChannel + if (state == ST_CLOSING) { + stateLock.notifyAll(); + } + } + // remove hook for Thread.interrupt + end(completed); + } + } + + @Override + public int write(ByteBuffer buf) throws IOException { + Objects.requireNonNull(buf); + + writeLock.lock(); + try { + boolean blocking = isBlocking(); + int n = 0; + try { + beginWrite(blocking); + if (blocking) { + do { + n = IOUtil.write(fd, buf, -1, nd); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = IOUtil.write(fd, buf, -1, nd); + } + } finally { + endWrite(blocking, n > 0); + if (n <= 0 && isOutputClosed) + throw new AsynchronousCloseException(); + } + return IOStatus.normalize(n); + } finally { + writeLock.unlock(); + } + } + + @Override + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + Objects.checkFromIndexSize(offset, length, srcs.length); + + writeLock.lock(); + try { + boolean blocking = isBlocking(); + long n = 0; + try { + beginWrite(blocking); + if (blocking) { + do { + n = IOUtil.write(fd, srcs, offset, length, nd); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = IOUtil.write(fd, srcs, offset, length, nd); + } + } finally { + endWrite(blocking, n > 0); + if (n <= 0 && isOutputClosed) + throw new AsynchronousCloseException(); + } + return IOStatus.normalize(n); + } finally { + writeLock.unlock(); + } + } + + int sendOutOfBandData(byte b) throws IOException { + writeLock.lock(); + try { + boolean blocking = isBlocking(); + int n = 0; + try { + beginWrite(blocking); + if (blocking) { + do { + n = sendOutOfBandData(fd, b); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } else { + n = sendOutOfBandData(fd, b); + } + } finally { + endWrite(blocking, n > 0); + if (n <= 0 && isOutputClosed) + throw new AsynchronousCloseException(); + } + return IOStatus.normalize(n); + } finally { + writeLock.unlock(); + } + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + readLock.lock(); + try { + writeLock.lock(); + try { + synchronized (stateLock) { + ensureOpen(); + RdmaNet.configureBlocking(fd, block); + } + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + } + + InetSocketAddress localAddress() { + synchronized (stateLock) { + return localAddress; + } + } + + InetSocketAddress remoteAddress() { + synchronized (stateLock) { + return remoteAddress; + } + } + + @Override + public SocketChannel bind(SocketAddress local) throws IOException { + readLock.lock(); + try { + writeLock.lock(); + try { + synchronized (stateLock) { + ensureOpen(); + if (state == ST_CONNECTIONPENDING) + throw new ConnectionPendingException(); + if (localAddress != null) + throw new AlreadyBoundException(); + InetSocketAddress isa = (local == null) ? + new InetSocketAddress(0) : RdmaNet.checkAddress(local); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkListen(isa.getPort()); + } + RdmaNet.bind(fd, isa.getAddress(), isa.getPort()); + localAddress = RdmaNet.localAddress(fd); + } + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + return this; + } + + @Override + public boolean isConnected() { + return (state == ST_CONNECTED); + } + + @Override + public boolean isConnectionPending() { + return (state == ST_CONNECTIONPENDING); + } + + private void beginConnect(boolean blocking, InetSocketAddress isa) + throws IOException + { + if (blocking) { + // set hook for Thread.interrupt + begin(); + } + synchronized (stateLock) { + ensureOpen(); + int state = this.state; + if (state == ST_CONNECTED) + throw new AlreadyConnectedException(); + if (state == ST_CONNECTIONPENDING) + throw new ConnectionPendingException(); + assert state == ST_UNCONNECTED; + this.state = ST_CONNECTIONPENDING; + + remoteAddress = isa; + + if (blocking) { + // record thread so it can be signalled if needed + readerThread = NativeThread.current(); + } + } + } + + private void endConnect(boolean blocking, boolean completed) + throws IOException + { + endRead(blocking, completed); + + if (completed) { + synchronized (stateLock) { + if (state == ST_CONNECTIONPENDING) { + localAddress = RdmaNet.localAddress(fd); + state = ST_CONNECTED; + } + } + } + } + + @Override + public boolean connect(SocketAddress sa) throws IOException { + InetSocketAddress isa = RdmaNet.checkAddress(sa); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); + + InetAddress ia = isa.getAddress(); + if (ia.isAnyLocalAddress()) + ia = InetAddress.getLocalHost(); + + try { + readLock.lock(); + try { + writeLock.lock(); + try { + int n = 0; + boolean blocking = isBlocking(); + try { + beginConnect(blocking, isa); + do { + n = RdmaNet.connect(fd, ia, isa.getPort()); + } while (n == IOStatus.INTERRUPTED && isOpen()); + } finally { + endConnect(blocking, (n > 0)); + } + assert IOStatus.check(n); + return n > 0; + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + } catch (IOException ioe) { + // connect failed, close the channel + close(); + throw ioe; + } + } + + private void beginFinishConnect(boolean blocking) throws ClosedChannelException { + if (blocking) { + // set hook for Thread.interrupt + begin(); + } + synchronized (stateLock) { + ensureOpen(); + if (state != ST_CONNECTIONPENDING) + throw new NoConnectionPendingException(); + if (blocking) { + // record thread so it can be signalled if needed + readerThread = NativeThread.current(); + } + } + } + + private void endFinishConnect(boolean blocking, boolean completed) + throws IOException + { + endRead(blocking, completed); + + if (completed) { + synchronized (stateLock) { + if (state == ST_CONNECTIONPENDING) { + localAddress = RdmaNet.localAddress(fd); + state = ST_CONNECTED; + } + } + } + } + + @Override + public boolean finishConnect() throws IOException { + try { + readLock.lock(); + try { + writeLock.lock(); + try { + // no-op if already connected + if (isConnected()) + return true; + + boolean blocking = isBlocking(); + boolean connected = false; + try { + beginFinishConnect(blocking); + int n = 0; + if (blocking) { + do { + n = checkConnect(fd, true); + } while ((n == 0 || n == IOStatus.INTERRUPTED) && isOpen()); + } else { + n = checkConnect(fd, false); + } + connected = (n > 0); + } finally { + endFinishConnect(blocking, connected); + } + assert (blocking && connected) ^ !blocking; + return connected; + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + } catch (IOException ioe) { + // connect failed, close the channel + close(); + throw ioe; + } + } + + @Override + protected void implCloseSelectableChannel() throws IOException { + assert !isOpen(); + + boolean blocking; + boolean connected; + boolean interrupted = false; + + // set state to ST_CLOSING + synchronized (stateLock) { + assert state < ST_CLOSING; + blocking = isBlocking(); + connected = (state == ST_CONNECTED); + state = ST_CLOSING; + } + + // wait for any outstanding I/O operations to complete + if (blocking) { + synchronized (stateLock) { + assert state == ST_CLOSING; + long reader = readerThread; + long writer = writerThread; + if (reader != 0 || writer != 0) { + nd.preClose(fd); + connected = false; // fd is no longer connected socket + + 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; + } + } + } + } + } else { + // non-blocking mode: wait for read/write to complete + readLock.lock(); + try { + writeLock.lock(); + writeLock.unlock(); + } finally { + readLock.unlock(); + } + } + + // set state to ST_KILLPENDING + synchronized (stateLock) { + assert state == ST_CLOSING; + // if connected and the channel is registered with a Selector then + // shutdown the output if possible so that the peer reads EOF. If + // SO_LINGER is enabled and set to a non-zero value then it needs to + // be disabled so that the Selector does not wait when it closes + // the socket. + if (connected && isRegistered()) { + try { + SocketOption opt = StandardSocketOptions.SO_LINGER; + int interval = (int) RdmaNet.getSocketOption(fd, RdmaNet.UNSPEC, opt); + if (interval != 0) { + if (interval > 0) { + // disable SO_LINGER + RdmaNet.setSocketOption(fd, RdmaNet.UNSPEC, opt, -1); + } + RdmaNet.shutdown(fd, RdmaNet.SHUT_WR); + } + } catch (IOException ignore) { } + } + state = ST_KILLPENDING; + } + + // close socket if not registered with Selector + if (!isRegistered()) + kill(); + + // restore interrupt status + if (interrupted) + Thread.currentThread().interrupt(); + } + + @Override + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ST_KILLPENDING) { + state = ST_KILLED; + nd.close(fd); + } + } + } + + @Override + public SocketChannel shutdownInput() throws IOException { + synchronized (stateLock) { + ensureOpen(); + if (!isConnected()) + throw new NotYetConnectedException(); + if (!isInputClosed) { + RdmaNet.shutdown(fd, RdmaNet.SHUT_RD); + long thread = readerThread; + if (thread != 0) + NativeThread.signal(thread); + isInputClosed = true; + } + return this; + } + } + + @Override + public SocketChannel shutdownOutput() throws IOException { + synchronized (stateLock) { + ensureOpen(); + if (!isConnected()) + throw new NotYetConnectedException(); + if (!isOutputClosed) { + RdmaNet.shutdown(fd, RdmaNet.SHUT_WR); + long thread = writerThread; + if (thread != 0) + NativeThread.signal(thread); + isOutputClosed = true; + } + return this; + } + } + + boolean isInputOpen() { + return !isInputClosed; + } + + boolean isOutputOpen() { + return !isOutputClosed; + } + + /** + * Poll this channel's socket for reading up to the given timeout. + * @return {@code true} if the socket is polled + */ + boolean pollRead(long timeout) throws IOException { + boolean blocking = isBlocking(); + assert Thread.holdsLock(blockingLock()) && blocking; + + readLock.lock(); + try { + boolean polled = false; + try { + beginRead(blocking); + int events = RdmaNet.poll(fd, RdmaNet.POLLIN, timeout); + polled = (events != 0); + } finally { + endRead(blocking, polled); + } + return polled; + } finally { + readLock.unlock(); + } + } + + /** + * Poll this channel's socket for a connection, up to the given timeout. + * @return {@code true} if the socket is polled + */ + boolean pollConnected(long timeout) throws IOException { + boolean blocking = isBlocking(); + assert Thread.holdsLock(blockingLock()) && blocking; + + readLock.lock(); + try { + writeLock.lock(); + try { + boolean polled = false; + try { + beginFinishConnect(blocking); + int events = RdmaNet.poll(fd, RdmaNet.POLLCONN, timeout); + polled = (events != 0); + } finally { + // invoke endFinishConnect with completed = false so that + // the state is not changed to ST_CONNECTED. The socket + // adaptor will use finishConnect to finish. + endFinishConnect(blocking, /*completed*/false); + } + return polled; + } finally { + writeLock.unlock(); + } + } finally { + readLock.unlock(); + } + } + + public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) { + int intOps = ski.nioInterestOps(); + int oldOps = ski.nioReadyOps(); + int newOps = initialOps; + + if ((ops & Net.POLLNVAL) != 0) { + return false; + } + + if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { + newOps = intOps; + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + boolean connected = isConnected(); + if (((ops & Net.POLLIN) != 0) && + ((intOps & SelectionKey.OP_READ) != 0) && connected) + newOps |= SelectionKey.OP_READ; + + if (((ops & Net.POLLCONN) != 0) && + ((intOps & SelectionKey.OP_CONNECT) != 0) && isConnectionPending()) + newOps |= SelectionKey.OP_CONNECT; + + if (((ops & Net.POLLOUT) != 0) && + ((intOps & SelectionKey.OP_WRITE) != 0) && connected) + newOps |= SelectionKey.OP_WRITE; + + ski.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, ski.nioReadyOps(), ski); + } + + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) { + return translateReadyOps(ops, 0, ski); + } + + public int translateInterestOps(int ops) { + int newOps = 0; + if ((ops & SelectionKey.OP_READ) != 0) + newOps |= Net.POLLIN; + if ((ops & SelectionKey.OP_WRITE) != 0) + newOps |= Net.POLLOUT; + if ((ops & SelectionKey.OP_CONNECT) != 0) + newOps |= Net.POLLCONN; + return newOps; + } + + public FileDescriptor getFD() { + return fd; + } + + public int getFDVal() { + return fdVal; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getSuperclass().getName()); + sb.append('['); + if (!isOpen()) + sb.append("closed"); + else { + synchronized (stateLock) { + switch (state) { + case ST_UNCONNECTED: + sb.append("unconnected"); + break; + case ST_CONNECTIONPENDING: + sb.append("connection-pending"); + break; + case ST_CONNECTED: + sb.append("connected"); + if (isInputClosed) + sb.append(" ishut"); + if (isOutputClosed) + sb.append(" oshut"); + break; + } + InetSocketAddress addr = localAddress(); + if (addr != null) { + sb.append(" local="); + sb.append(RdmaNet.getRevealedLocalAddressAsString(addr)); + } + if (remoteAddress() != null) { + sb.append(" remote="); + sb.append(remoteAddress().toString()); + } + } + } + sb.append(']'); + return sb.toString(); + } + + // -- Native methods -- + + private static native void initIDs(); + + private static native int checkConnect(FileDescriptor fd, boolean block) + throws IOException; + + private static native int sendOutOfBandData(FileDescriptor fd, byte data) + throws IOException; + + static { + IOUtil.load(); + initIDs(); + nd = new RdmaSocketDispatcher(); + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketDispatcher.java 2018-06-22 12:23:59.453671859 -0700 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import sun.nio.ch.SocketDispatcher; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.io.*; + +/** + * Allows different platforms to call different native methods + * for read and write operations. + */ + +public class RdmaSocketDispatcher extends SocketDispatcher +{ + private static PlatformRdmaSocketDispatcher platformRdmaSocketDispatcher = + PlatformRdmaSocketDispatcher.get(); + + protected int read(FileDescriptor fd, long address, int len) throws IOException { + return platformRdmaSocketDispatcher.read(fd, address, len); + } + + protected long readv(FileDescriptor fd, long address, int len) throws IOException { + return platformRdmaSocketDispatcher.readv(fd, address, len); + } + + protected int write(FileDescriptor fd, long address, int len) throws IOException { + return platformRdmaSocketDispatcher.write(fd, address, len); + } + + protected long writev(FileDescriptor fd, long address, int len) throws IOException { + return platformRdmaSocketDispatcher.writev(fd, address, len); + } + + protected void close(FileDescriptor fd) throws IOException { + platformRdmaSocketDispatcher.close(fd); + } + + public void preClose(FileDescriptor fd) throws IOException { + platformRdmaSocketDispatcher.preClose(fd); + } + + static class PlatformRdmaSocketDispatcher { + @SuppressWarnings("unchecked") + private static PlatformRdmaSocketDispatcher newInstance(String cn) { + Class c; + try { + c = (Class)Class.forName(cn); + return c.getConstructor(new Class[] {}).newInstance(); + } catch (ReflectiveOperationException x) { + throw new AssertionError(x); + } + } + + private static PlatformRdmaSocketDispatcher create() { + String osname = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("os.name"); + } + }); + if ("Linux".equals(osname)) + return newInstance("rdma.ch.LinuxRdmaSocketDispatcher"); + return new PlatformRdmaSocketDispatcher(); + } + + private static final PlatformRdmaSocketDispatcher instance = create(); + + static PlatformRdmaSocketDispatcher get() { + return instance; + } + + protected int read(FileDescriptor fd, long address, int len) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + protected long readv(FileDescriptor fd, long address, int len) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + protected int write(FileDescriptor fd, long address, int len) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + protected long writev(FileDescriptor fd, long address, int len) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + protected void close(FileDescriptor fd) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + + public void preClose(FileDescriptor fd) throws IOException { + throw new UnsupportedOperationException("unsupported socket operation"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketImpl.java 2018-06-22 12:24:00.049671843 -0700 @@ -0,0 +1,798 @@ +/* + * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileDescriptor; + +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; +import java.net.Socket; +import java.net.ServerSocket; +import java.net.SocketImpl; +import java.net.SocketOption; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.net.StandardSocketOptions; +import java.net.SocketOptions; +import java.lang.reflect.Field; +import sun.net.ConnectionResetException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.net.ext.RdmaSocketOptions; + +public class RdmaSocketImpl extends SocketImpl +{ + Socket socket = null; + ServerSocket serverSocket = null; + + int timeout; // timeout in millisec + + int trafficClass; + + private boolean shut_rd = false; + private boolean shut_wr = false; + + private RdmaSocketInputStream socketInputStream = null; + private RdmaSocketOutputStream socketOutputStream = null; + + /* number of threads using the FileDescriptor */ + protected int fdUseCount = 0; + + /* lock when increment/decrementing fdUseCount */ + protected final Object fdLock = new Object(); + + /* indicates a close is pending on the file descriptor */ + protected boolean closePending = false; + + /* indicates connection reset state */ + private int CONNECTION_NOT_RESET = 0; + private int CONNECTION_RESET_PENDING = 1; + private int CONNECTION_RESET = 2; + private int resetState; + private final Object resetLock = new Object(); + + /* whether this Socket is a stream (TCP) socket or not (UDP) + */ + protected boolean stream; + + static final sun.net.ext.RdmaSocketOptions rdmaOptions = + sun.net.ext.RdmaSocketOptions.getInstance(); + + private static PlatformRdmaSocketImpl platformRdmaSocketImpl = + PlatformRdmaSocketImpl.get(); + + private static Field sCreateState; + private static Field sBoundState; + private static Field sConnectState; + private static Field ssCreateState; + private static Field ssBoundState; + private static boolean socketStateSet; + private static boolean serverSocketStateSet; + + boolean isRdmaAvailable() { + return platformRdmaSocketImpl.isRdmaAvailable(); + } + + @Override + protected void setSocket(Socket soc) { + this.socket = soc; + try { + if (!socketStateSet) { + sCreateState = Socket.class.getDeclaredField("created"); + sCreateState.setAccessible(true); + sBoundState = Socket.class.getDeclaredField("bound"); + sBoundState.setAccessible(true); + sConnectState= Socket.class.getDeclaredField("connected"); + sConnectState.setAccessible(true); + sCreateState.setBoolean(socket, false); + sBoundState.setBoolean(socket, false); + sConnectState.setBoolean(socket, false); + } + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new Error(e); + } + socketStateSet = true; + } + + Socket getSocket() { + return socket; + } + + @Override + protected void setServerSocket(ServerSocket soc) { + this.serverSocket = soc; + try { + if (!serverSocketStateSet) { + ssCreateState = ServerSocket.class.getDeclaredField("created"); + ssCreateState.setAccessible(true); + ssBoundState = ServerSocket.class.getDeclaredField("bound"); + ssBoundState.setAccessible(true); + ssCreateState.setBoolean(serverSocket, false); + ssBoundState.setBoolean(serverSocket, false); + } + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new Error(e); + } + serverSocketStateSet = true; + } + + ServerSocket getServerSocket() { + return serverSocket; + } + + private static final Set> socketOptions; + + private static final Set> serverSocketOptions; + + static { + socketOptions = Set.of(StandardSocketOptions.SO_SNDBUF, + StandardSocketOptions.SO_RCVBUF, + StandardSocketOptions.SO_REUSEADDR, + StandardSocketOptions.SO_LINGER, + StandardSocketOptions.TCP_NODELAY); + + serverSocketOptions = Set.of(StandardSocketOptions.SO_RCVBUF, + StandardSocketOptions.SO_REUSEADDR); + } + + @Override + protected Set> supportedOptions() { + Set> options = new HashSet<>(); + if (socket != null) + options.addAll(socketOptions); + else + options.addAll(serverSocketOptions); + + if (isRdmaAvailable()) { + RdmaSocketOptions rdmaOptions = + RdmaSocketOptions.getInstance(); + options.addAll(rdmaOptions.options()); + } + options = Collections.unmodifiableSet(options); + return options; + } + + protected synchronized void create(boolean stream) throws IOException { + this.stream = stream; + if (stream) { + fd = new FileDescriptor(); + platformRdmaSocketImpl.rdmaSocketCreate(true, this); + } + try { + if (socket != null) { + sCreateState.setBoolean(socket, true); + } + if (serverSocket != null) + ssCreateState.setBoolean(serverSocket, true); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + protected void connect(String host, int port) + throws UnknownHostException, IOException + { + boolean connected = false; + try { + InetAddress address = InetAddress.getByName(host); + this.port = port; + this.address = address; + + connectToAddress(address, port, timeout); + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + } + } + } + } + + protected void connect(InetAddress address, int port) throws IOException { + this.port = port; + this.address = address; + + try { + connectToAddress(address, port, timeout); + return; + } catch (IOException e) { + close(); + throw e; + } + } + + protected void connect(SocketAddress address, int timeout) + throws IOException { + boolean connected = false; + try { + if (address == null || !(address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + InetSocketAddress addr = (InetSocketAddress) address; + if (addr.isUnresolved()) + throw new UnknownHostException(addr.getHostName()); + this.port = addr.getPort(); + this.address = addr.getAddress(); + + connectToAddress(this.address, port, timeout); + connected = true; + } finally { + if (!connected) { + try { + close(); + } catch (IOException ioe) { + } + } + } + } + + private void connectToAddress(InetAddress address, int port, int timeout) throws IOException { + if (address.isAnyLocalAddress()) { + doConnect(InetAddress.getLocalHost(), port, timeout); + } else { + doConnect(address, port, timeout); + } + } + + protected void setOption(SocketOption name, T value) throws IOException { + if (!rdmaOptions.isOptionSupported(name)) { + int opt; + if (name == StandardSocketOptions.SO_SNDBUF && + socket != null) { + if (socket.isConnected()) + throw new UnsupportedOperationException( + "RDMA socket cannot set SO_SNDBUF after connect."); + opt = SocketOptions.SO_SNDBUF; + } else if (name == StandardSocketOptions.SO_RCVBUF) { + if (socket != null && socket.isConnected()) + throw new UnsupportedOperationException( + "RDMA socket cannot set SO_RCVBUF after connect."); + if (serverSocket != null && serverSocket.isBound()) + throw new UnsupportedOperationException( + "RDMA server socket cannot set SO_RCVBUF after bind."); + opt = SocketOptions.SO_RCVBUF; + } else if (name == StandardSocketOptions.SO_REUSEADDR) { + opt = SocketOptions.SO_REUSEADDR; + } else if (name == StandardSocketOptions.TCP_NODELAY && + (socket != null)) { + opt = SocketOptions.TCP_NODELAY; + } else if (name == StandardSocketOptions.SO_LINGER && + (socket != null)) { + opt = SocketOptions.SO_LINGER; + } else { + throw new UnsupportedOperationException("unsupported option"); + } + setOption(opt, value); + } else { + rdmaOptions.setOption(fd, name, value); + } + } + + @SuppressWarnings("unchecked") + protected T getOption(SocketOption name) throws IOException { + if (!rdmaOptions.isOptionSupported(name)) { + int opt; + if (name == StandardSocketOptions.SO_SNDBUF && + (socket != null)) { + opt = SocketOptions.SO_SNDBUF; + } else if (name == StandardSocketOptions.SO_RCVBUF) { + opt = SocketOptions.SO_RCVBUF; + } else if (name == StandardSocketOptions.SO_REUSEADDR) { + opt = SocketOptions.SO_REUSEADDR; + } else if (name == StandardSocketOptions.SO_LINGER && + (socket != null)) { + return (T)getOption(SocketOptions.SO_LINGER); + } else if (name == StandardSocketOptions.TCP_NODELAY && + (socket != null)) { + opt = SocketOptions.TCP_NODELAY; + } else { + throw new UnsupportedOperationException("unsupported option"); + } + return (T) getOption(opt); + } else { + return (T) rdmaOptions.getOption(fd, name); + } + } + + public void setOption(int opt, Object val) throws SocketException { + if (isClosedOrPending()) { + throw new SocketException("Socket Closed"); + } + boolean on = true; + switch (opt) { + case SO_LINGER: + if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean))) + throw new SocketException("Bad parameter for option"); + if (val instanceof Boolean) { + /* true only if disabling - enabling should be Integer */ + on = false; + } + break; + case SO_TIMEOUT: + if (val == null || (!(val instanceof Integer))) + throw new SocketException("Bad parameter for SO_TIMEOUT"); + int tmp = ((Integer) val).intValue(); + if (tmp < 0) + throw new IllegalArgumentException("timeout < 0"); + timeout = tmp; + break; + case SO_BINDADDR: + throw new SocketException("Cannot re-bind socket"); + case TCP_NODELAY: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for TCP_NODELAY"); + on = ((Boolean)val).booleanValue(); + break; + case SO_SNDBUF: + case SO_RCVBUF: + int value = ((Integer)val).intValue(); + int maxValue = 1024 * 1024 * 1024 - 1; //maximum value for the buffer + if (val == null || !(val instanceof Integer) || + !(value > 0)) { + throw new SocketException("bad parameter for SO_SNDBUF " + + "or SO_RCVBUF"); + } + if (value >= maxValue) + value = maxValue; + break; + case SO_REUSEADDR: + if (val == null || !(val instanceof Boolean)) + throw new SocketException("bad parameter for SO_REUSEADDR"); + on = ((Boolean)val).booleanValue(); + if (serverSocket != null && serverSocket.isBound()) + throw new UnsupportedOperationException( + "RDMA server socket cannot set " + + "SO_REUSEADDR after bind."); + if (socket != null && socket.isConnected()) + throw new UnsupportedOperationException( + "RDMA socket cannot set " + + "SO_REUSEADDR after connect."); + break; + default: + throw new SocketException("unrecognized TCP option: " + opt); + } + socketSetOption(opt, on, val); + } + + public Object getOption(int opt) throws SocketException { + if (isClosedOrPending()) { + throw new SocketException("Socket Closed"); + } + if (opt == SO_TIMEOUT) { + return timeout; + } + int ret = 0; + + switch (opt) { + case TCP_NODELAY: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return Boolean.valueOf(ret != -1); + case SO_LINGER: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return (ret == -1) ? Boolean.FALSE: (Object)(ret); + case SO_REUSEADDR: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return Boolean.valueOf(ret != -1); + case SO_BINDADDR: + RdmaInetAddressContainer in = new RdmaInetAddressContainer(); + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, in); + return in.addr; + case SO_SNDBUF: + case SO_RCVBUF: + ret = platformRdmaSocketImpl.rdmaSocketGetOption(this, opt, null); + return ret; + default: + return null; + } + } + + protected void socketSetOption(int opt, boolean b, Object val) throws SocketException { + if (opt == SocketOptions.SO_REUSEPORT && + !supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) { + throw new UnsupportedOperationException("unsupported option"); + } + try { + platformRdmaSocketImpl.rdmaSocketSetOption(this, opt, b, val); + } catch (SocketException se) { + if (socket == null || !socket.isConnected()) + throw se; + } + } + + synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException { + try { + acquireFD(); + try { + platformRdmaSocketImpl.rdmaSocketConnect(this, address, port, timeout); + synchronized (fdLock) { + if (closePending) { + throw new SocketException ("Socket closed"); + } + } + try { + if (socket != null) { + sBoundState.setBoolean(socket, true); + sConnectState.setBoolean(socket, true); + } + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } finally { + releaseFD(); + } + } catch (IOException e) { + close(); + throw e; + } + } + + protected synchronized void bind(InetAddress address, int lport) + throws IOException + { + platformRdmaSocketImpl.rdmaSocketBind(this, address, lport); + try { + if (socket != null) + sBoundState.setBoolean(socket, true); + if (serverSocket != null) + ssBoundState.setBoolean(serverSocket, true); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + protected synchronized void listen(int count) throws IOException { + platformRdmaSocketImpl.rdmaSocketListen(this, count); + } + + protected void accept(SocketImpl s) throws IOException { + acquireFD(); + try { + platformRdmaSocketImpl.rdmaSocketAccept(s, this); + } finally { + releaseFD(); + } + } + + protected synchronized InputStream getInputStream() throws IOException { + synchronized (fdLock) { + if (isClosedOrPending()) + throw new IOException("Socket Closed"); + if (shut_rd) + throw new IOException("Socket input is shutdown"); + if (socketInputStream == null) + socketInputStream = new RdmaSocketInputStream(this); + } + return socketInputStream; + } + + void setInputStream(RdmaSocketInputStream in) { + socketInputStream = in; + } + + protected synchronized OutputStream getOutputStream() throws IOException { + synchronized (fdLock) { + if (isClosedOrPending()) + throw new IOException("Socket Closed"); + if (shut_wr) + throw new IOException("Socket output is shutdown"); + if (socketOutputStream == null) + socketOutputStream = new RdmaSocketOutputStream(this); + } + return socketOutputStream; + } + + protected FileDescriptor getFileDescriptor() { + return fd; + } + + protected void setFileDescriptor(FileDescriptor fd) { + this.fd = fd; + } + + protected void setAddress(InetAddress address) { + this.address = address; + } + + void setPort(int port) { + this.port = port; + } + + void setLocalPort(int localport) { + this.localport = localport; + } + + protected synchronized int available() throws IOException { + if (isClosedOrPending()) { + throw new IOException("Stream closed."); + } + + if (isConnectionReset() || shut_rd) { + return 0; + } + + int n = 0; + try { + n = platformRdmaSocketImpl.rdmaSocketAvailable(this); + if (n == 0 && isConnectionResetPending()) { + setConnectionReset(); + } + } catch (ConnectionResetException exc1) { + setConnectionResetPending(); + try { + n = platformRdmaSocketImpl.rdmaSocketAvailable(this); + if (n == 0) { + setConnectionReset(); + } + } catch (ConnectionResetException exc2) { + } + } + return n; + } + + protected void close() throws IOException { + synchronized(fdLock) { + if (fd != null) { + if (fdUseCount == 0) { + if (closePending) { + return; + } + closePending = true; + try { + platformRdmaSocketImpl.rdmaSocketClose(true, this); + } finally { + platformRdmaSocketImpl.rdmaSocketClose(false, this); + } + fd = null; + return; + } else { + if (!closePending) { + closePending = true; + fdUseCount--; + platformRdmaSocketImpl.rdmaSocketClose(true, this); + } + } + } + } + } + + void reset() throws IOException { + if (fd != null) { + platformRdmaSocketImpl.rdmaSocketClose(false, this); + } + fd = null; + postReset(); + } + + void postReset() throws IOException { + address = null; + port = 0; + localport = 0; + } + + protected void shutdownInput() throws IOException { + if (fd != null) { + platformRdmaSocketImpl.rdmaSocketShutdown(SHUT_RD, this); + if (socketInputStream != null) { + socketInputStream.setEOF(true); + } + shut_rd = true; + } + } + + protected void shutdownOutput() throws IOException { + if (fd != null) { + platformRdmaSocketImpl.rdmaSocketShutdown(SHUT_WR, this); + shut_wr = true; + } + } + + protected boolean supportsUrgentData () { + return true; + } + + protected void sendUrgentData (int data) throws IOException { + if (fd == null) { + throw new IOException("Socket Closed"); + } + platformRdmaSocketImpl.rdmaSocketSendUrgentData(this, data); + } + + FileDescriptor acquireFD() { + synchronized (fdLock) { + fdUseCount++; + return fd; + } + } + + void releaseFD() { + synchronized (fdLock) { + fdUseCount--; + if (fdUseCount == -1) { + if (fd != null) { + try { + platformRdmaSocketImpl.rdmaSocketClose(false, this); + } catch (IOException e) { + } finally { + fd = null; + } + } + } + } + } + + public boolean isConnectionReset() { + synchronized (resetLock) { + return (resetState == CONNECTION_RESET); + } + } + + public boolean isConnectionResetPending() { + synchronized (resetLock) { + return (resetState == CONNECTION_RESET_PENDING); + } + } + + public void setConnectionReset() { + synchronized (resetLock) { + resetState = CONNECTION_RESET; + } + } + + public void setConnectionResetPending() { + synchronized (resetLock) { + if (resetState == CONNECTION_NOT_RESET) { + resetState = CONNECTION_RESET_PENDING; + } + } + + } + + public boolean isClosedOrPending() { + synchronized (fdLock) { + if (closePending || (fd == null)) { + return true; + } else { + return false; + } + } + } + + public int getTimeout() { + return timeout; + } + + protected InetAddress getInetAddress() { + return address; + } + + protected int getPort() { + return port; + } + + protected int getLocalPort() { + return localport; + } + + public static final int SHUT_RD = 0; + public static final int SHUT_WR = 1; + + static class PlatformRdmaSocketImpl { + + @SuppressWarnings("unchecked") + private static PlatformRdmaSocketImpl newInstance(String cn) { + Class c; + try { + c = (Class)Class.forName(cn); + return c.getConstructor(new Class[] {}).newInstance(); + } catch (ReflectiveOperationException x) { + throw new AssertionError(x); + } + } + + private static PlatformRdmaSocketImpl create() { + String osname = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("os.name"); + } + }); + if ("Linux".equals(osname)) + return newInstance("rdma.ch.LinuxRdmaSocketImpl"); + return new PlatformRdmaSocketImpl(); + } + + private static final PlatformRdmaSocketImpl instance = create(); + + static PlatformRdmaSocketImpl get() { + return instance; + } + + boolean isRdmaAvailable() { + return false; + } + + void rdmaSocketClose(boolean useDeferredClose, RdmaSocketImpl impl) throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketCreate(boolean isServer, RdmaSocketImpl impl) throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketConnect(RdmaSocketImpl impl, InetAddress address, int port, int timeout) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketBind(RdmaSocketImpl impl, InetAddress address, int port) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketListen(RdmaSocketImpl impl, int count) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketAccept(SocketImpl s, RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + int rdmaSocketAvailable(RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketShutdown(int howto, RdmaSocketImpl impl) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketSetOption(RdmaSocketImpl impl, int cmd, boolean on, Object value) + throws SocketException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + int rdmaSocketGetOption(RdmaSocketImpl impl, int opt, Object iaContainerObj) + throws SocketException { + throw new UnsupportedOperationException("unsupported socket option"); + } + + void rdmaSocketSendUrgentData(RdmaSocketImpl impl, int data) + throws IOException { + throw new UnsupportedOperationException("unsupported socket option"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketInputStream.java 2018-06-22 12:24:00.626671827 -0700 @@ -0,0 +1,210 @@ +/* + * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.net.Socket; +import java.net.SocketException; + +import sun.net.ConnectionResetException; + +class RdmaSocketInputStream extends FileInputStream +{ + static { + init(); + } + + private boolean eof; + private RdmaSocketImpl impl = null; + private byte temp[]; + private Socket socket = null; + + RdmaSocketInputStream(RdmaSocketImpl impl) throws IOException { + super(impl.getFileDescriptor()); + this.impl = impl; + socket = impl.getSocket(); + } + + public final FileChannel getChannel() { + return null; + } + + private native int rdmaSocketRead0(FileDescriptor fd, + byte b[], int off, int len, + int timeout) + throws IOException; + + + private int rdmaSocketRead(FileDescriptor fd, + byte b[], int off, int len, + int timeout) + throws IOException { + return rdmaSocketRead0(fd, b, off, len, timeout); + } + + public int read(byte b[]) throws IOException { + return read(b, 0, b.length); + } + + public int read(byte b[], int off, int length) throws IOException { + return read(b, off, length, impl.getTimeout()); + } + + int read(byte b[], int off, int length, int timeout) throws IOException { + int n; + + if (eof) { + return -1; + } + + if (impl.isConnectionReset()) { + throw new SocketException("Connection reset"); + } + + if (length <= 0 || off < 0 || length > b.length - off) { + if (length == 0) { + return 0; + } + throw new ArrayIndexOutOfBoundsException("length == " + length + + " off == " + off + " buffer length == " + b.length); + } + + boolean gotReset = false; + + FileDescriptor fd = impl.acquireFD(); + try { + n = rdmaSocketRead(fd, b, off, length, timeout); + if (n > 0) { + return n; + } + } catch (ConnectionResetException rstExc) { + gotReset = true; + } finally { + impl.releaseFD(); + } + + if (gotReset) { + impl.setConnectionResetPending(); + impl.acquireFD(); + try { + n = rdmaSocketRead(fd, b, off, length, timeout); + if (n > 0) { + return n; + } + } catch (ConnectionResetException rstExc) { + } finally { + impl.releaseFD(); + } + } + + if (impl.isClosedOrPending()) { + throw new SocketException("Socket closed"); + } + if (impl.isConnectionResetPending()) { + impl.setConnectionReset(); + } + if (impl.isConnectionReset()) { + throw new SocketException("Connection reset"); + } + eof = true; + return -1; + } + + /** + * Reads a single byte from the socket. + */ + public int read() throws IOException { + if (eof) { + return -1; + } + temp = new byte[1]; + int n = read(temp, 0, 1); + if (n <= 0) { + return -1; + } + return temp[0] & 0xff; + } + + /** + * Skips n bytes of input. + * @param numbytes the number of bytes to skip + * @return the actual number of bytes skipped. + * @exception IOException If an I/O error has occurred. + */ + public long skip(long numbytes) throws IOException { + if (numbytes <= 0) { + return 0; + } + long n = numbytes; + int buflen = (int) Math.min(1024, n); + byte data[] = new byte[buflen]; + while (n > 0) { + int r = read(data, 0, (int) Math.min((long) buflen, n)); + if (r < 0) { + break; + } + n -= r; + } + return numbytes - n; + } + + /** + * Returns the number of bytes that can be read without blocking. + * @return the number of immediately available bytes + */ + public int available() throws IOException { + return impl.available(); + } + + /** + * Closes the stream. + */ + private boolean closing = false; + public void close() throws IOException { + // Prevent recursion. See BugId 4484411 + if (closing) + return; + closing = true; + if (socket != null) { + if (!socket.isClosed()) + socket.close(); + } else + impl.close(); + closing = false; + } + + void setEOF(boolean eof) { + this.eof = eof; + } + + /** + * Perform class load-time initializations. + */ + private static native void init(); +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketOptionRegistry.java.template 2018-06-22 12:24:01.150671813 -0700 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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. + * + */ +@@END_COPYRIGHT@@ + +#include +#include +#include +#include +#include + +/* To be able to name the Java constants the same as the C constants without + having the preprocessor rewrite those identifiers, add PREFIX_ to all + identifiers matching a C constant. The PREFIX_ is filtered out in the + makefile. */ + +@@START_HERE@@ + +package rdma.ch; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.net.ProtocolFamily; +import java.net.StandardProtocolFamily; +import java.util.Map; +import java.util.HashMap; +import sun.nio.ch.ExtendedSocketOption; + +class RdmaSocketOptionRegistry { + + private RdmaSocketOptionRegistry() { } + + private static class RegistryKey { + private final SocketOption name; + private final ProtocolFamily family; + RegistryKey(SocketOption name, ProtocolFamily family) { + this.name = name; + this.family = family; + } + public int hashCode() { + return name.hashCode() + family.hashCode(); + } + public boolean equals(Object ob) { + if (ob == null) return false; + if (!(ob instanceof RegistryKey)) return false; + RegistryKey other = (RegistryKey)ob; + if (this.name != other.name) return false; + if (this.family != other.family) return false; + return true; + } + } + + private static class LazyInitialization { + + static final Map options = options(); + + private static Map options() { + Map map = + new HashMap(); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_SO_LINGER, + RdmaNet.UNSPEC), new RdmaOptionKey(SOL_SOCKET, SO_LINGER)); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_SO_SNDBUF, + RdmaNet.UNSPEC), new RdmaOptionKey(SOL_SOCKET, SO_SNDBUF)); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_SO_RCVBUF, + RdmaNet.UNSPEC), new RdmaOptionKey(SOL_SOCKET, SO_RCVBUF)); + map.put(new RegistryKey(StandardSocketOptions.PREFIX_SO_REUSEADDR, + RdmaNet.UNSPEC), new RdmaOptionKey(SOL_SOCKET, SO_REUSEADDR)); + // IPPROTO_TCP is 6 + map.put(new RegistryKey(StandardSocketOptions.PREFIX_TCP_NODELAY, + RdmaNet.UNSPEC), new RdmaOptionKey(6, TCP_NODELAY)); + return map; + } + } + + public static RdmaOptionKey findOption(SocketOption name, ProtocolFamily family) { + RegistryKey key = new RegistryKey(name, family); + return LazyInitialization.options.get(key); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/src/jdk.net/share/classes/rdma/ch/RdmaSocketOutputStream.java 2018-06-22 12:24:01.690671799 -0700 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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 rdma.ch; + +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.net.Socket; +import java.net.SocketException; + +class RdmaSocketOutputStream extends FileOutputStream +{ + static { + init(); + } + + private RdmaSocketImpl impl = null; + private byte temp[] = new byte[1]; + private Socket socket = null; + + RdmaSocketOutputStream(RdmaSocketImpl impl) throws IOException { + super(impl.getFileDescriptor()); + this.impl = impl; + socket = impl.getSocket(); + } + + public final FileChannel getChannel() { + return null; + } + + /** + * Writes to the socket. + * @param fd the FileDescriptor + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception IOException If an I/O error has occurred. + */ + private native void rdmaSocketWrite0(FileDescriptor fd, byte[] b, int off, + int len) throws IOException; + + /** + * Writes to the socket with appropriate locking of the + * FileDescriptor. + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception IOException If an I/O error has occurred. + */ + private void rdmaSocketWrite(byte b[], int off, int len) throws IOException { + + + if (len <= 0 || off < 0 || len > b.length - off) { + if (len == 0) { + return; + } + throw new ArrayIndexOutOfBoundsException("len == " + len + + " off == " + off + " buffer length == " + b.length); + } + + FileDescriptor fd = impl.acquireFD(); + try { + rdmaSocketWrite0(fd, b, off, len); + } catch (SocketException se) { + if (se instanceof sun.net.ConnectionResetException) { + impl.setConnectionResetPending(); + se = new SocketException("Connection reset"); + } + if (impl.isClosedOrPending()) { + throw new SocketException("Socket closed"); + } else { + throw se; + } + } finally { + impl.releaseFD(); + } + } + + /** + * Writes a byte to the socket. + * @param b the data to be written + * @exception IOException If an I/O error has occurred. + */ + public void write(int b) throws IOException { + temp[0] = (byte)b; + rdmaSocketWrite(temp, 0, 1); + } + + /** + * Writes the contents of the buffer b to the socket. + * @param b the data to be written + * @exception SocketException If an I/O error has occurred. + */ + public void write(byte b[]) throws IOException { + rdmaSocketWrite(b, 0, b.length); + } + + /** + * Writes length bytes from buffer b starting at + * offset len. + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception SocketException If an I/O error has occurred. + */ + public void write(byte b[], int off, int len) throws IOException { + rdmaSocketWrite(b, off, len); + } + + /** + * Closes the stream. + */ + private boolean closing = false; + public void close() throws IOException { + if (closing) + return; + closing = true; + if (socket != null) { + if (!socket.isClosed()) + socket.close(); + } else + impl.close(); + closing = false; + } + + /** + * Perform class load-time initializations. + */ + private static native void init(); + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/RsocketTest.java 2018-06-22 12:24:02.277671783 -0700 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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. + */ + +/* + * @test + * @bug 8195160 + * @summary Test to check if rsocket is available + * @requires (os.family == "linux") + * @library /test/lib + * @build jdk.test.lib.Platform + * @run main/native RsocketTest + */ + +import java.net.*; +import jdk.net.Sockets; + +public class RsocketTest { + private static boolean supported; + private static boolean checked; + + static { + System.loadLibrary("RsocketTest"); + } + + public static boolean isRsocketAvailable() { + if (!checked) { + checked = true; + supported = isRsocketAvailable0(); + } + return supported; + } + + private static native boolean isRsocketAvailable0(); + + public static void main(String[] args) throws Exception { + System.out.println("testing rsocket!"); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/BasicAccept.java 2018-06-22 12:24:02.822671768 -0700 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test RdmaSelector with RdmaServerSocketChannels + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicAccept + */ + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.channels.spi.SelectorProvider; +import java.util.*; +import jdk.net.Sockets; + +public class BasicAccept { + + static void server(ServerSocketChannel ssc) throws Exception { + Selector acceptSelector = Sockets.openRdmaSelector(); + try { + ssc.configureBlocking(false); + SelectionKey acceptKey + = ssc.register(acceptSelector, SelectionKey.OP_ACCEPT); + for (;;) { + int n = acceptSelector.select(); + if (Thread.interrupted()) + break; + if (n == 0) + continue; + Set readyKeys = acceptSelector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = i.next(); + i.remove(); + ServerSocketChannel nextReady + = (ServerSocketChannel)sk.channel(); + SocketChannel sc = nextReady.accept(); + ByteBuffer bb = ByteBuffer.wrap(new byte[] { 42 }); + sc.write(bb); + sc.close(); + } + } + } finally { + acceptSelector.close(); + } + } + + private static class Server extends TestThread { + final ServerSocketChannel ssc; + Server() throws IOException { + super("Server", System.err); + this.ssc = Sockets.openRdmaServerSocketChannel() + .bind(new InetSocketAddress(InetAddress.getLocalHost(), 0)); + } + int port() { + return ssc.socket().getLocalPort(); + } + void go() throws Exception { + try { + server(ssc); + } finally { + ssc.close(); + } + } + } + + static void client(int port) throws Exception { + // Get a connection from the server + InetAddress lh = InetAddress.getLocalHost(); + InetSocketAddress isa + = new InetSocketAddress(lh, port); + int connectFailures = 0; + boolean result = false; + SocketChannel sc = Sockets.openRdmaSocketChannel(); + for (;;) { + try { + result = sc.connect(isa); + break; + } catch (java.net.ConnectException e) { + connectFailures++; + if (connectFailures > 30) + throw new RuntimeException("Cannot connect"); + Thread.currentThread().sleep(100); + sc = Sockets.openRdmaSocketChannel(); + } + } + if (result) { + System.err.println("Connected"); + } else { + // Only happens when server and client are on separate machines + System.err.println("Connection pending..."); + connectFailures = 0; + while (!result) { + try { + result = sc.finishConnect(); + if (!result) + System.err.println("Not finished"); + Thread.sleep(50); + } catch (java.net.ConnectException e) { + Thread.sleep(100); + connectFailures++; + if (connectFailures > 30) + throw new RuntimeException("Cannot finish connecting"); + } + } + System.err.println("Finished connecting"); + } + + ByteBuffer bb = ByteBuffer.allocateDirect(1024); + if (sc.read(bb) < 0) + throw new RuntimeException("Failed to read from server"); + if (bb.get(0) != 42) + throw new RuntimeException("Read wrong byte from server"); + System.err.println("Read from server"); + sc.close(); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + Server server = new Server(); + server.start(); + try { + client(server.port()); + } finally { + server.interrupt(); + server.finish(2000); + } + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/BasicConnect.java 2018-06-22 12:24:03.322671755 -0700 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test for nonblocking connect and finishConnect + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicConnect + */ + +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.channels.spi.SelectorProvider; +import java.util.*; +import jdk.net.Sockets; + +/** + * Typically there would be more than one channel registered to select + * on, this test is just a very simple version with only one channel + * registered for the connectSelector. + */ + +public class BasicConnect { + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + Selector connectSelector = + Sockets.openRdmaSelector(); + try (TestServers.EchoServer echoServer + = TestServers.EchoServer.startNewServer(100)) { + InetSocketAddress isa + = new InetSocketAddress(echoServer.getAddress(), + echoServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + boolean result = sc.connect(isa); + if (result) { + System.out.println("Socket immediately connected on " + + System.getProperty("os.name") + + ": " + sc); + } + while (!result) { + SelectionKey connectKey = sc.register(connectSelector, + SelectionKey.OP_CONNECT); + int keysAdded = connectSelector.select(); + if (keysAdded > 0) { + Set readyKeys = connectSelector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + i.remove(); + SocketChannel nextReady = (SocketChannel)sk.channel(); + result = nextReady.finishConnect(); + if (result) + sk.cancel(); + } + } + } + + byte[] bs = new byte[] { (byte)0xca, (byte)0xfe, + (byte)0xba, (byte)0xbe }; + ByteBuffer bb = ByteBuffer.wrap(bs); + sc.configureBlocking(true); + sc.write(bb); + bb.rewind(); + + ByteBuffer bb2 = ByteBuffer.allocateDirect(100); + int n = sc.read(bb2); + bb2.flip(); + + sc.close(); + connectSelector.close(); + + if (!bb.equals(bb2)) + throw new Exception("Echoed bytes incorrect: Sent " + + bb + ", got " + bb2); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/ByteServer.java 2018-06-22 12:24:03.878671740 -0700 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Utility class for tests. A simple "in-thread" server to accept connections + * and write bytes. + */ + +import java.net.Socket; +import java.net.ServerSocket; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.io.Closeable; +import jdk.net.Sockets; + +public class ByteServer implements Closeable { + + private final ServerSocket ss; + private Socket s; + + ByteServer() throws IOException { + ServerSocket ss = Sockets.openRdmaServerSocket(); + ss.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0)); + this.ss = ss; + } + + SocketAddress address() { + return new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort()); + } + + void acceptConnection() throws IOException { + if (s != null) + throw new IllegalStateException("already connected"); + this.s = ss.accept(); + } + + void closeConnection() throws IOException { + Socket s = this.s; + if (s != null) { + this.s = null; + s.close(); + } + } + + void write(int count) throws IOException { + if (s == null) + throw new IllegalStateException("no connection"); + s.getOutputStream().write(new byte[count]); + } + + public void close() throws IOException { + if (s != null) + s.close(); + ss.close(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/Close.java 2018-06-22 12:24:04.470671724 -0700 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test for keys remaining in RDMA selector after channel closed + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Close + */ +import java.nio.*; +import java.nio.channels.*; +import jdk.net.Sockets; + +public class Close { + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + Selector sa = Sockets.openRdmaSelector(); + Selector sb = Sockets.openRdmaSelector(); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + SelectionKey sk = sc.register(sa, SelectionKey.OP_READ); + sc.register(sb, SelectionKey.OP_READ); + sc.keyFor(sa).cancel(); + sa.select(1); + sc.close(); + sa.select(1); + sb.select(1); + if (sa.keys().size() > 0) + throw new RuntimeException("Keys remain in selector a"); + if (sb.keys().size() > 0) + throw new RuntimeException("Keys remain in selector b"); + sa.close(); + sb.close(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/Connect.java 2018-06-22 12:24:05.003671709 -0700 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test Test Making lots of Selectors + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Connect + */ + +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.channels.spi.SelectorProvider; +import java.util.*; +import jdk.net.Sockets; + +public class Connect implements Runnable { + + static int success = 0; + static int LIMIT = 30; + static SocketChannel sc; + static InetSocketAddress isa; + static boolean connected; + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer daytimeServer + = TestServers.DayTimeServer.startNewServer(50)) { + scaleTest(daytimeServer); + } + } + + static void scaleTest(TestServers.DayTimeServer daytimeServer) + throws Exception + { + InetAddress myAddress = daytimeServer.getAddress(); + isa = new InetSocketAddress(myAddress, daytimeServer.getPort()); + + for (int j=0; j 0) { + Set readyKeys = RSelector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = i.next(); + SocketChannel nextReady = (SocketChannel)sk.channel(); + connected = nextReady.finishConnect(); + } + readyKeys.clear(); + } + } + RSelector.close(); + } + readAndClose(sc); + } + } + + static void readAndClose(SocketChannel sc) throws Exception { + ByteBuffer bb = ByteBuffer.allocateDirect(100); + int n = 0; + while (n == 0) // Note this is not a rigorous check for done reading + n = sc.read(bb); + sc.close(); + success++; + System.out.println("success count = " + success); + } + + public void run() { + try { + connected = sc.connect(isa); + System.out.println("connected = " + connected); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/ConnectWrite.java 2018-06-22 12:24:05.546671695 -0700 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test ready for connect followed by ready for write + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true ConnectWrite + */ + +import java.net.*; +import java.nio.channels.*; +import java.nio.channels.spi.SelectorProvider; +import java.util.*; +import jdk.net.Sockets; + +public class ConnectWrite { + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer daytimeServer + = TestServers.DayTimeServer.startNewServer(25)) { + test1(daytimeServer); + } + } + + static void test1(TestServers.DayTimeServer daytimeServer) throws Exception { + Selector selector = Sockets.openRdmaSelector(); + InetAddress myAddress = daytimeServer.getAddress(); + InetSocketAddress isa + = new InetSocketAddress(myAddress, daytimeServer.getPort()); + + SocketChannel sc = Sockets.openRdmaSocketChannel(); + + try { + sc.configureBlocking(false); + SelectionKey key = sc.register(selector, SelectionKey.OP_CONNECT); + boolean result = sc.connect(isa); + + while (!result) { + int keysAdded = selector.select(1000); + if (keysAdded > 0) { + Set readyKeys = selector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + readyKeys.remove(sk); + SocketChannel nextReady = (SocketChannel)sk.channel(); + result = nextReady.finishConnect(); + } + } + } + if (key != null) { + key.interestOps(SelectionKey.OP_WRITE); + int keysAdded = selector.select(1000); + if (keysAdded <= 0) + throw new Exception("connect->write failed"); + if (keysAdded > 0) { + Set readyKeys = selector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + if (!sk.isWritable()) + throw new Exception("connect->write failed"); + } + } + } + } finally { + sc.close(); + selector.close(); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/KeysReady.java 2018-06-22 12:24:06.112671680 -0700 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test if keys reported ready multiple times + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true KeysReady + */ + +import java.net.*; +import java.nio.channels.*; +import java.nio.channels.spi.SelectorProvider; +import jdk.net.Sockets; + +public class KeysReady { + + static void test(TestServers.DayTimeServer dayTimeServer) throws Exception { + InetSocketAddress isa + = new InetSocketAddress(dayTimeServer.getAddress(), + dayTimeServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + sc.connect(isa); + + // Prepare a selector + Selector selector = Sockets.openRdmaSelector(); + try { + SelectionKey key = sc.register(selector, SelectionKey.OP_CONNECT); + int keysAdded = selector.select(); + if (keysAdded > 0) { + keysAdded = selector.select(1000); + if (keysAdded > 0) + throw new Exception("Same key reported added twice"); + } + } finally { + selector.close(); + sc.close(); + } + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer daytimeServer + = TestServers.DayTimeServer.startNewServer(50)) { + test(daytimeServer); + } + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/OpRead.java 2018-06-22 12:24:06.615671666 -0700 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test if OP_READ is detected with OP_WRITE in interestOps + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true OpRead + */ + +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import jdk.net.Sockets; + +public class OpRead implements Runnable { + + static ServerSocketChannel ssc = null; + static SocketChannel sc = null; + static SocketChannel peer = null; + static InetAddress lh; + + static void test() throws Exception { + try { + lh = InetAddress.getLocalHost(); + ssc = Sockets.openRdmaServerSocketChannel().bind(new InetSocketAddress(lh, 0)); + + // loopback connection + sc = Sockets.openRdmaSocketChannel(); + Thread t = new Thread(new OpRead()); + t.start(); + + peer = ssc.accept(); + + // peer sends message so that "sc" will be readable + int n = peer.write(ByteBuffer.wrap("Hello".getBytes())); + assert n > 0; + + sc.configureBlocking(false); + + Selector selector = Sockets.openRdmaSelector(); + SelectionKey key = sc.register(selector, SelectionKey.OP_READ | + SelectionKey.OP_WRITE); + + boolean done = false; + int failCount = 0; + while (!done) { + int nSelected = selector.select(); + if (nSelected > 0) { + if (nSelected > 1) + throw new RuntimeException("More than one channel selected"); + Set keys = selector.selectedKeys(); + Iterator iterator = keys.iterator(); + while (iterator.hasNext()) { + key = iterator.next(); + iterator.remove(); + if (key.isWritable()) { + failCount++; + if (failCount > 10) + throw new RuntimeException("Test failed"); + Thread.sleep(250); + } + if (key.isReadable()) { + done = true; + } + } + } + } + } finally { + if (peer != null) peer.close(); + if (sc != null) sc.close(); + if (ssc != null) ssc.close(); + } + } + + public void run() { + try { + sc.connect(new InetSocketAddress(lh, ssc.socket().getLocalPort())); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + test(); + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/ReadAfterConnect.java 2018-06-22 12:24:07.146671652 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Check blocking of select and close + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true ReadAfterConnect + */ + +import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import jdk.net.Sockets; + +public class ReadAfterConnect implements Runnable { + + static ByteServer server; + static SocketChannel sc; + + public static void main(String[] argv) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try { + server = new ByteServer(); + sc = Sockets.openRdmaSocketChannel(); + + Thread t = new Thread (new ReadAfterConnect()); + t.start(); + + server.acceptConnection(); + + try (Selector sel = Sockets.openRdmaSelector()) { + sc.configureBlocking(false); + sc.register(sel, SelectionKey.OP_READ); + // Previously channel would get selected here, although there is nothing to read + if (sel.selectNow() != 0) + throw new Exception("Select returned nonzero value"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Select did not awaken!"); + } + } + + public void run() { + try { + sc.connect(server.address()); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/RegisterDuringSelect.java 2018-06-22 12:24:07.647671638 -0700 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test that RDMA channels can be registered, interest ops can changed, + * and keys cancelled while a selection operation is in progress. + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicAccept + */ +import java.io.IOException; +import java.nio.channels.Pipe; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.Phaser; +import jdk.net.Sockets; + +public class RegisterDuringSelect { + + static Callable selectLoop(Selector sel, Phaser barrier) { + return new Callable() { + @Override + public Void call() throws IOException { + for (;;) { + sel.select(); + if (sel.isOpen()) { + barrier.arriveAndAwaitAdvance(); + } else { + // closed + return null; + } + } + } + }; + } + /** + * Invoke register, interestOps, and cancel concurrently with a thread + * doing a selection operation + */ + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + Future result; + + ExecutorService pool = Executors.newFixedThreadPool(1); + try (Selector sel = Sockets.openRdmaSelector()) { + Phaser barrier = new Phaser(2); + + // submit task to do the select loop + result = pool.submit(selectLoop(sel, barrier)); + + Pipe p = Pipe.open(); + try { + Pipe.SourceChannel sc = p.source(); + sc.configureBlocking(false); + + System.out.println("register ..."); + SelectionKey key = sc.register(sel, SelectionKey.OP_READ); + if (!sel.keys().contains(key)) + throw new RuntimeException("key not in key set"); + sel.wakeup(); + barrier.arriveAndAwaitAdvance(); + + System.out.println("interestOps ..."); + key.interestOps(0); + sel.wakeup(); + barrier.arriveAndAwaitAdvance(); + + System.out.println("cancel ..."); + key.cancel(); + sel.wakeup(); + barrier.arriveAndAwaitAdvance(); + if (sel.keys().contains(key)) + throw new RuntimeException("key not removed from key set"); + + } finally { + p.source().close(); + p.sink().close(); + } + + } finally { + pool.shutdown(); + } + + // ensure selectLoop completes without exception + result.get(); + + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/SelectAndClose.java 2018-06-22 12:24:08.180671624 -0700 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Check blocking of select and close + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true SelectAndClose + */ + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.MonitorInfo; +import java.lang.management.ThreadInfo; +import java.nio.channels.Selector; +import jdk.net.Sockets; + +public class SelectAndClose { + static Selector selector; + static volatile boolean awakened = false; + + private static boolean mightHoldLock(Thread t, Object lock) { + long tid = t.getId(); + int hash = System.identityHashCode(lock); + ThreadInfo ti = ManagementFactory.getThreadMXBean(). + getThreadInfo(new long[]{ tid} , true, false, 100)[0]; + if (ti != null) { + for (MonitorInfo mi : ti.getLockedMonitors()) { + if (mi.getIdentityHashCode() == hash) + return true; + } + } + return false; + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + selector = Sockets.openRdmaSelector(); + + // Create and start a selector in a separate thread. + Thread selectThread = new Thread(new Runnable() { + public void run() { + try { + selector.select(); + awakened = true; + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException("Select did not awaken!"); + } + } + }); + selectThread.start(); + + // Spin until the monitor of the selected-key set is likely held + // as selected operations are specified to synchronize on the + // selected-key set. + while (!mightHoldLock(selectThread, selector.selectedKeys())) { + Thread.sleep(50); + } + + // Close the selector. + selector.close(); + + if (!awakened) + selector.wakeup(); + + // Wait for select() thread to finish. + selectThread.join(); + + if (!awakened) { + throw new RuntimeException("Select did not awaken!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/SelectWrite.java 2018-06-22 12:24:08.716671610 -0700 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test RdmaSocket with OP_WRITE would get selected only once + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true SelectWrite + */ + +import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import jdk.net.Sockets; + +public class SelectWrite implements Runnable { + + static SocketChannel sc; + static ByteServer server; + + public static void main(String[] argv) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try { + server = new ByteServer(); + sc = Sockets.openRdmaSocketChannel(); + + Thread t = new Thread(new SelectWrite()); + t.start(); + + server.acceptConnection(); + + try (Selector sel = Sockets.openRdmaSelector()) { + sc.configureBlocking(false); + sc.register(sel, SelectionKey.OP_WRITE); + sel.select(); + sel.selectedKeys().clear(); + if (sel.select() == 0) { + throw new Exception("Select returned zero"); + } + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } + + public void run() { + try { + sc.connect(server.address()); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Selector/SelectorTest.java 2018-06-22 12:24:09.251671595 -0700 @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test RdmaSelector with RdmaServerSocketChannels + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicAccept + */ + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.channels.spi.SelectorProvider; +import java.util.*; +import jdk.net.Sockets; + +public class SelectorTest { + private static List clientList = new LinkedList(); + private static Random rnd = new Random(); + public static int NUM_CLIENTS = 5; + public static int TEST_PORT = 31452; + static PrintStream log = System.err; + private static int FINISH_TIME = 30000; + + /* + * Usage note + * + * java SelectorTest [server] [client ] [] + * + * No arguments runs both client and server in separate threads + * using the default port of 31452. + * + * client runs the client on this machine and connects to server + * at the given IP address. + * + * server runs the server on localhost. + */ + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + if (args.length == 0) { + Server server = new Server(0); + server.start(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { } + InetSocketAddress isa + = new InetSocketAddress(InetAddress.getLocalHost(), server.port()); + Client client = new Client(isa); + client.start(); + if ((server.finish(FINISH_TIME) & client.finish(FINISH_TIME)) == 0) + throw new Exception("Failure"); + log.println(); + + } else if (args[0].equals("server")) { + + if (args.length > 1) + TEST_PORT = Integer.parseInt(args[1]); + Server server = new Server(TEST_PORT); + server.start(); + if (server.finish(FINISH_TIME) == 0) + throw new Exception("Failure"); + log.println(); + + } else if (args[0].equals("client")) { + + if (args.length < 2) { + log.println("No host specified: terminating."); + return; + } + String ip = args[1]; + if (args.length > 2) + TEST_PORT = Integer.parseInt(args[2]); + InetAddress ia = InetAddress.getByName(ip); + InetSocketAddress isa = new InetSocketAddress(ia, TEST_PORT); + Client client = new Client(isa); + client.start(); + if (client.finish(FINISH_TIME) == 0) + throw new Exception("Failure"); + log.println(); + + } else { + System.out.println("Usage note:"); + System.out.println("java SelectorTest [server] [client ] []"); + System.out.println("No arguments runs both client and server in separate threads using the default port of 31452."); + System.out.println("client runs the client on this machine and connects to the server specified."); + System.out.println("server runs the server on localhost."); + } + } + + static class Client extends TestThread { + InetSocketAddress isa; + Client(InetSocketAddress isa) { + super("Client", SelectorTest.log); + this.isa = isa; + } + + public void go() throws Exception { + log.println("starting client..."); + for (int i=0; i 0); + } + } + + static class Server extends TestThread { + private final ServerSocketChannel ssc; + private List socketList = new ArrayList(); + private ServerSocket ss; + private int connectionsAccepted = 0; + private Selector pollSelector; + private Selector acceptSelector; + private Set pkeys; + private Set pskeys; + + Server(int port) throws IOException { + super("Server", SelectorTest.log); + this.ssc = Sockets.openRdmaServerSocketChannel(); + ssc.bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + } + + int port() { + return ssc.socket().getLocalPort(); + } + + public void go() throws Exception { + log.println("starting server..."); + acceptSelector = Sockets.openRdmaSelector(); + pollSelector = Sockets.openRdmaSelector(); + pkeys = pollSelector.keys(); + pskeys = pollSelector.selectedKeys(); + Set readyKeys = acceptSelector.selectedKeys(); + RequestHandler rh = new RequestHandler(pollSelector, log); + Thread requestThread = new Thread(rh); + + requestThread.start(); + + ssc.configureBlocking(false); + SelectionKey acceptKey = ssc.register(acceptSelector, + SelectionKey.OP_ACCEPT); + while(connectionsAccepted < SelectorTest.NUM_CLIENTS) { + int keysAdded = acceptSelector.select(100); + if (keysAdded > 0) { + Iterator i = readyKeys.iterator(); + while(i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + i.remove(); + ServerSocketChannel nextReady = + (ServerSocketChannel)sk.channel(); + SocketChannel sc = nextReady.accept(); + connectionsAccepted++; + if (sc != null) { + sc.configureBlocking(false); + synchronized (pkeys) { + sc.register(pollSelector, SelectionKey.OP_READ); + } + } else { + throw new RuntimeException( + "Socket does not support Channels"); + } + } + } + } + acceptKey.cancel(); + requestThread.join(); + acceptSelector.close(); + pollSelector.close(); + } + } +} + +class RemoteEntity { + private static Random rnd = new Random(); + int id; + ByteBuffer data; + int dataWrittenIndex; + int totalDataLength; + boolean initiated = false; + boolean connected = false; + boolean written = false; + boolean acked = false; + boolean closed = false; + private SocketChannel sc; + ByteBuffer ackBuffer; + PrintStream log; + InetSocketAddress server; + + RemoteEntity(int id, InetSocketAddress server, PrintStream log) + throws Exception + { + int connectFailures = 0; + this.id = id; + this.log = log; + this.server = server; + + sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + + // Prepare the data buffer to write out from this entity + // Let's use both slow and fast buffers + if (rnd.nextBoolean()) + data = ByteBuffer.allocateDirect(100); + else + data = ByteBuffer.allocate(100); + String number = Integer.toString(id); + if (number.length() == 1) + number = "0"+number; + String source = "Testing from " + number; + data.put(source.getBytes("8859_1")); + data.flip(); + totalDataLength = source.length(); + + // Allocate an ack buffer + ackBuffer = ByteBuffer.allocateDirect(10); + } + + private void reset() throws Exception { + sc.close(); + sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + } + + private void connect() throws Exception { + try { + connected = sc.connect(server); + initiated = true; + } catch (ConnectException e) { + initiated = false; + reset(); + } + } + + private void finishConnect() throws Exception { + try { + connected = sc.finishConnect(); + } catch (IOException e) { + initiated = false; + reset(); + } + } + + int id() { + return id; + } + + boolean cycle() throws Exception { + if (!initiated) + connect(); + else if (!connected) + finishConnect(); + else if (!written) + writeCycle(); + else if (!acked) + ackCycle(); + else if (!closed) + close(); + return closed; + } + + private void ackCycle() throws Exception { + //log.println("acking from "+id); + int bytesRead = sc.read(ackBuffer); + if (bytesRead > 0) { + acked = true; + } + } + + private void close() throws Exception { + sc.close(); + closed = true; + } + + private void writeCycle() throws Exception { + log.println("writing from "+id); + int numBytesToWrite = rnd.nextInt(10)+1; + int newWriteTarget = dataWrittenIndex + numBytesToWrite; + if (newWriteTarget > totalDataLength) + newWriteTarget = totalDataLength; + data.limit(newWriteTarget); + int bytesWritten = sc.write(data); + if (bytesWritten > 0) + dataWrittenIndex += bytesWritten; + if (dataWrittenIndex == totalDataLength) { + written = true; + sc.socket().shutdownOutput(); + } + } + +} + + +class RequestHandler implements Runnable { + private static Random rnd = new Random(); + private Selector selector; + private int connectionsHandled = 0; + private HashMap dataBin = new HashMap(); + PrintStream log; + + public RequestHandler(Selector selector, PrintStream log) { + this.selector = selector; + this.log = log; + } + + public void run() { + log.println("starting request handler..."); + int connectionsAccepted = 0; + + Set nKeys = selector.keys(); + Set readyKeys = selector.selectedKeys(); + + try { + while(connectionsHandled < SelectorTest.NUM_CLIENTS) { + int numKeys = selector.select(100); + + // Process channels with data + synchronized (nKeys) { + if (readyKeys.size() > 0) { + Iterator i = readyKeys.iterator(); + while(i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + i.remove(); + SocketChannel sc = (SocketChannel)sk.channel(); + if (sc.isOpen()) + read(sk, sc); + } + } + } + + // Give other threads a chance to run + if (numKeys == 0) { + try { + Thread.sleep(1); + } catch (Exception x) {} + } + } + } catch (Exception e) { + log.println("Unexpected error 1: "+e); + e.printStackTrace(); + } + } + + private void read(SelectionKey sk, SocketChannel sc) throws Exception { + ByteBuffer bin = (ByteBuffer)dataBin.get(sc); + if (bin == null) { + if (rnd.nextBoolean()) + bin = ByteBuffer.allocateDirect(100); + else + bin = ByteBuffer.allocate(100); + dataBin.put(sc, bin); + } + + int bytesRead = 0; + do { + bytesRead = sc.read(bin); + } while(bytesRead > 0); + + if (bytesRead == -1) { + sk.interestOps(0); + bin.flip(); + int size = bin.limit(); + byte[] data = new byte[size]; + for(int j=0; j>>"+message + "<<<"); + log.println("Handled: "+connectionsHandled); + } + } + + private void acknowledge(SocketChannel sc) throws Exception { + ByteBuffer ackBuffer = ByteBuffer.allocateDirect(10); + String s = "ack"; + ackBuffer.put(s.getBytes("8859_1")); + ackBuffer.flip(); + int bytesWritten = 0; + while(bytesWritten == 0) { + bytesWritten += sc.write(ackBuffer); + } + sc.close(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/ServerSocketChannel/AdaptServerSocket.java 2018-06-22 12:24:09.830671580 -0700 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for server-socket-channel adaptors + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true AdaptServerSocket + */ + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.charset.*; +import jdk.net.Sockets; + +public class AdaptServerSocket { + + static java.io.PrintStream out = System.out; + static volatile boolean clientStarted = false; + static volatile Exception clientException = null; + static volatile Thread client = null; + + static void startClient(final int port, final int dally) + throws Exception + { + Thread t = new Thread() { + public void run() { + try { + Socket so = Sockets.openRdmaSocket(); + out.println("client: " + so); + clientStarted = true; + if (dally > 0) + Thread.sleep(dally); + so.connect(new InetSocketAddress(port)); + if (Thread.interrupted()) { + out.println("client interrupted"); + return; + } + out.println("client: " + so); + int a = so.getInputStream().read(); + out.println("client: read " + a); + a += 1; + so.getOutputStream().write(a); + out.println("client: wrote " + a); + } catch (Exception x) { + if (x instanceof InterruptedException) + return; + clientException = x; + x.printStackTrace(); + } + } + }; + t.setDaemon(true); + t.start(); + client = t; + } + + static void test(int clientDally, int timeout, boolean shouldTimeout) + throws Exception + { + boolean needClient = !shouldTimeout; + client = null; + clientException = null; + clientStarted = false; + out.println(); + + try { + ServerSocketChannel ssc = Sockets.openRdmaServerSocketChannel(); + ServerSocket sso = ssc.socket(); + out.println("created: " + ssc); + out.println(" " + sso); + if (timeout != 0) + sso.setSoTimeout(timeout); + out.println("timeout: " + sso.getSoTimeout()); + sso.bind(null); + out.println("bound: " + ssc); + out.println(" " + sso); + if (needClient) { + startClient(sso.getLocalPort(), clientDally); + while (!clientStarted) { + Thread.sleep(20); + } + } + Socket so = null; + try { + so = sso.accept(); + } catch (SocketTimeoutException x) { + if (shouldTimeout) + out.println("Accept timed out, as expected"); + else + throw x; + } + if (shouldTimeout && (so != null)) + throw new Exception("Accept did not time out"); + + if (so != null) { + int a = 42; + so.getOutputStream().write(a); + int b = so.getInputStream().read(); + if (b != a + 1) + throw new Exception("Read incorrect data"); + out.println("server: read " + b); + } + } catch (Exception e) { + e.printStackTrace(); + throw new Exception("Test Failed!"); + } + + if (needClient) { + client.interrupt(); + client.join(); + if (clientException != null) + throw clientException; + } + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + test(0, 0, false); + test(50, 5000, false); + test(500, 50, true); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/ServerSocketChannel/Basic.java 2018-06-22 12:24:10.369671565 -0700 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for server-socket channels + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Basic + */ + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.Random; +import jdk.net.Sockets; + +public class Basic { + + static PrintStream log = System.err; + + static class Server + extends TestThread + { + ServerSocketChannel ssc; + boolean block; + + Server(ServerSocketChannel ssc, boolean block) { + super("Server", Basic.log); + this.ssc = ssc; + this.block = block; + } + + void go() throws Exception { + log.println("Server: Listening " + + (block ? "(blocking)" : "(non-blocking)")); + if (!block) + ssc.configureBlocking(false); + log.println(" " + ssc); + //log.println(" " + ssc.options()); + SocketChannel sc = null; + for (;;) { + sc = ssc.accept(); + if (sc != null) { + break; + } + log.println("Server: Sleeping..."); + Thread.sleep(50); + } + log.println("Server: Accepted " + sc); + ByteBuffer bb = ByteBuffer.allocateDirect(100); + if (sc.read(bb) != 1) + throw new Exception("Read failed"); + bb.flip(); + byte b = bb.get(); + log.println("Server: Read " + b + ", writing " + (b + 1)); + bb.clear(); + bb.put((byte)43); + bb.flip(); + if (sc.write(bb) != 1) + throw new Exception("Write failed"); + sc.close(); + ssc.close(); + log.println("Server: Finished"); + } + + } + + static class Client + extends TestThread + { + int port; + boolean dally; + + Client(int port, boolean block) { + super("Client", Basic.log); + this.port = port; + this.dally = !block; + } + + public void go() throws Exception { + if (dally) + Thread.sleep(200); + InetSocketAddress isa + = new InetSocketAddress(InetAddress.getLocalHost(), port); + log.println("Client: Connecting to " + isa); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.connect(isa); + log.println("Client: Connected"); + ByteBuffer bb = ByteBuffer.allocateDirect(512); + bb.put((byte)42).flip(); + log.println("Client: Writing " + bb.get(0)); + if (sc.write(bb) != 1) + throw new Exception("Write failed"); + bb.clear(); + if (sc.read(bb) != 1) + throw new Exception("Read failed"); + bb.flip(); + if (bb.get() != 43) + throw new Exception("Read " + bb.get(bb.position() - 1)); + log.println("Client: Read " + bb.get(0)); + sc.close(); + log.println("Client: Finished"); + } + + } + + static void test(boolean block) throws Exception { + ServerSocketChannel ssc = Sockets.openRdmaServerSocketChannel(); + ssc.socket().setReuseAddress(true); + InetAddress lh = InetAddress.getLocalHost(); + int port; + Random r = new Random(); + for (;;) { + port = r.nextInt((1 << 16) - 1024) + 1024; + InetSocketAddress isa = new InetSocketAddress(lh, port); + try { + ssc.socket().bind(isa); + } catch (IOException x) { + continue; + } + break; + } + + Server server = new Server(ssc, block); + Client client = new Client(port, block); + server.start(); + client.start(); + if ((server.finish(0) & client.finish(0)) == 0) + throw new Exception("Failure"); + log.println(); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + log.println(); + test(true); + test(false); + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/AddressTest.java 2018-06-22 12:24:10.949671549 -0700 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test various methods that should throw IAE when passed improper + * SocketAddress + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true AddressTest + */ + +import java.net.*; +import jdk.net.Sockets; + +public class AddressTest { + class MySocketAddress extends SocketAddress { + public MySocketAddress() { + } + } + + public AddressTest() throws Exception { + SocketAddress addr = new MySocketAddress(); + Socket soc = Sockets.openRdmaSocket(); + ServerSocket serv = Sockets.openRdmaServerSocket(); + boolean ok = false; + try { + soc.bind(addr); + } catch (IllegalArgumentException e) { + ok = true; + } catch (Exception e2) { + } + if (!ok) + throw new RuntimeException("Socket.bind should throw IllegalArgumentException!"); + + ok = false; + soc.close(); + soc = Sockets.openRdmaSocket(); + try { + soc.connect(addr, 100); + } catch (IllegalArgumentException e) { + ok = true; + } catch (Exception e2) { + } + if (!ok) + throw new RuntimeException("Socket.connect should throw IllegalArgumentException!"); + + ok = false; + try { + serv.bind(addr); + } catch (IllegalArgumentException e) { + ok = true; + } catch (Exception e2) { + } + if (!ok) + throw new RuntimeException("ServerSocket.bind should throw IllegalArgumentException!"); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + new AddressTest(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/BasicSocketTest.java 2018-06-22 12:24:11.483671535 -0700 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test for basic functionality for RdmaSocket and RdmaServerSocket + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicSocketTest + */ + +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.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import jdk.net.Sockets; + +public class BasicSocketTest implements Runnable { + static ServerSocket ss; + static Socket s1, s2; + static InetAddress iaddr; + static int port = 0; + static ServerSocketChannel ssc; + static SocketChannel sc1, sc2; + static String message = "This is a message!"; + static int length = -1; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + iaddr = InetAddress.getLocalHost(); + + try { + ss = Sockets.openRdmaServerSocket(); + s1 = Sockets.openRdmaSocket(); + ss.bind(new InetSocketAddress(iaddr, port)); + + Thread t = new Thread(new BasicSocketTest()); + t.start(); + s2 = ss.accept(); + + InputStream is = s2.getInputStream(); + length = message.length(); + + int num = 0; + byte[] buf = new byte[length]; + while (num < length) { + int l = is.read(buf); + num += l; + } + + String result = new String(buf); + if(!result.equals(message)) + throw new RuntimeException("Test Failed!"); + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } finally { + ss.close(); + s1.close(); + s2.close(); + } + } + + public void run() { + try { + s1.connect(new InetSocketAddress(iaddr, ss.getLocalPort())); + + OutputStream os = s1.getOutputStream(); + os.write(message.getBytes()); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/CloseAvailable.java 2018-06-22 12:24:12.015671521 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for socket channels + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true CloseAvailable + */ +import java.net.*; +import java.io.*; +import jdk.net.Sockets; + +public class CloseAvailable implements Runnable { + static ServerSocket ss; + static InetAddress addr; + static int port; + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + boolean error = true; + addr = InetAddress.getLocalHost(); + System.out.println(addr.getHostAddress()); + ss = Sockets.openRdmaServerSocket(); + ss.bind(new InetSocketAddress(addr, 0)); + port = ss.getLocalPort(); + + Thread t = new Thread(new CloseAvailable()); + t.start(); + + Socket soc = ss.accept(); + ss.close(); + + DataInputStream is = new DataInputStream(soc.getInputStream()); + is.close(); + + try { + is.available(); + } + catch (IOException ex) { + error = false; + } + if (error) + throw new RuntimeException("Available() can be called after stream closed."); + } + + public void run() { + try { + Socket s = Sockets.openRdmaSocket(); + s.connect(new InetSocketAddress(addr, port)); + s.close(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/DeadlockTest.java 2018-06-22 12:24:12.543671506 -0700 @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Make sure a deadlock situation would not occur + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true DeadlockTest + */ +import java.net.*; +import java.io.*; +import jdk.net.Sockets; + +public class DeadlockTest { + public static void main(String [] argv) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + InetAddress addr = InetAddress.getLocalHost(); + ServerSocket ss = Sockets.openRdmaServerSocket(); + ss.bind(new InetSocketAddress(addr, 0)); + + Socket clientSocket = Sockets.openRdmaSocket(); + + try { + // Start the server thread + Thread s1 = new Thread(new ServerThread(ss)); + s1.start(); + + // Start the client thread + ClientThread ct = new ClientThread(clientSocket, ss.getLocalPort()); + Thread c1 = new Thread(ct); + c1.start(); + + // Wait for the client thread to finish + c1.join(20000); + + // If timeout, we assume there is a deadlock + if (c1.isAlive() == true) { + // Close the socket to force the server thread + // terminate too + s1.stop(); + throw new Exception("Takes too long. Dead lock"); + } + } finally { + ss.close(); + clientSocket.close(); + } + } +} + +class ServerThread implements Runnable { + + private static boolean dbg = false; + + ObjectInputStream in; + ObjectOutputStream out; + + ServerSocket server; + + Socket sock; + + public ServerThread(ServerSocket serverSocket) throws Exception { + this.server = serverSocket; + } + + public void ping(int cnt) { + Message.write(out, new PingMessage(cnt)); + } + + private int cnt = 1; + + public void run() { + + try { + if (Thread.currentThread().getName().startsWith("child") == false) { + sock = server.accept(); + + new Thread(this, "child").start(); + + out = new ObjectOutputStream(sock.getOutputStream()); + out.flush(); + + if (dbg) System.out.println("*** ping0 ***"); + ping(0); + if (dbg) System.out.println("*** ping1 ***"); + ping(1); + if (dbg) System.out.println("*** ping2 ***"); + ping(2); + if (dbg) System.out.println("*** ping3 ***"); + ping(3); + if (dbg) System.out.println("*** ping4 ***"); + ping(4); + if (dbg) System.out.println("*** end ***"); + } + + } catch (Throwable e) { + System.out.println(e); + // If anything goes wrong, just quit. + } + + if (Thread.currentThread().getName().startsWith("child")) { + try { + + in = new ObjectInputStream(sock.getInputStream()); + + while (true) { + if (dbg) System.out.println("read " + cnt); + Message msg = (Message) in.readObject(); + if (dbg) System.out.println("read done " + cnt++); + switch (msg.code) { + case Message.PING: { + if (true) System.out.println("ping recv'ed"); + } break; + } + + } + + } catch (Throwable e) { + // If anything goes wrong, just quit. } + } + } + } +} + +class ClientThread implements Runnable { + + ObjectInputStream in; + ObjectOutputStream out; + + Socket sock; + + public ClientThread(Socket sock, int serverPort) throws Exception { + try { + System.out.println("About to connect the client socket"); + this.sock = sock; + this.sock.connect(new InetSocketAddress(InetAddress.getLocalHost(), serverPort)); + System.out.println("connected"); + + out = new ObjectOutputStream(sock.getOutputStream()); + out.flush(); + } catch (Throwable e) { + System.out.println("client failed with: " + e); + e.printStackTrace(); + throw new Exception("Unexpected exception"); + } + } + + private int cnt = 1; + + public void run() { + try { + in = new ObjectInputStream(sock.getInputStream()); + + int count = 0; + + while (true) { + System.out.println("read " + cnt); + Message msg = (Message) in.readObject(); + System.out.println("read done " + cnt++); + switch (msg.code) { + case Message.PING: { + System.out.println("ping recv'ed"); + count++; + } break; + } + if (count == 5) { + sock.close(); + break; + } + } + } catch (IOException ioe) { + } catch (Throwable e) { + // If anything went wrong, just quit + } + } + +} + +class Message implements java.io.Serializable { + + static final int UNKNOWN = 0; + static final int PING = 1; + + protected int code; + + public Message() { this.code = UNKNOWN; } + + public Message(int code) { this.code = code; } + + private static int cnt = 1; + + public static void write(ObjectOutput out, Message msg) { + try { + System.out.println("write message " + cnt); + out.writeObject(msg); + System.out.println("flush message"); + out.flush(); + System.out.println("write message done " + cnt++); + } catch (IOException ioe) { + // Ignore the exception + System.out.println(ioe); + } + } +} + +class PingMessage extends Message implements java.io.Serializable { + + public PingMessage() { + code = Message.PING; + } + + public PingMessage(int cnt) + { + code = Message.PING; + this.cnt = cnt; + + data = new int[50000]; + } + + int cnt; + int[] data; +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/GetLocalAddress.java 2018-06-22 12:24:13.086671492 -0700 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary REGRESSION: Socket.getLocalAddress() returns address of 0.0.0.0 on outbound TCP + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true GetLocalAddress + */ +import java.util.*; +import java.net.*; +import jdk.net.Sockets; + +public class GetLocalAddress implements Runnable { + + static Socket s; + static ServerSocket ss; + static InetAddress addr; + static InetAddress iaLocal; + static String sLocalHostname; + + public static void main(String[] args) throws Exception + { + if (!RsocketTest.isRsocketAvailable()) + return; + + ss = Sockets.openRdmaServerSocket(); + addr = InetAddress.getLocalHost(); + ss.bind(new InetSocketAddress(addr, 0)); + int port = ss.getLocalPort(); + + s = Sockets.openRdmaSocket(); + + try { + Thread t = new Thread(new GetLocalAddress()); + t.start(); + } catch(Exception e) { + System.out.println("Exception happened"); + throw e; + } finally { + ss.close(); + } + } + + public void run() { + try { + InetSocketAddress isa = new InetSocketAddress(addr, 0); + s.connect( isa, 1000 ); + + byte[] bad = {0,0,0,0}; + InetAddress iaLocal = s.getLocalAddress(); + String sLocalHostname = iaLocal.getHostName(); + if (Arrays.equals (iaLocal.getAddress(), bad)) { + throw new RuntimeException ("0.0.0.0 returned"); + } + System.out.println("local hostname is "+sLocalHostname ); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/GetLocalAddress1.java 2018-06-22 12:24:13.678671476 -0700 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test the java.net.socket.GetLocalAddress method + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true GetLocalAddress1 + */ + +import java.net.*; +import jdk.net.Sockets; + +public class GetLocalAddress1 implements Runnable { + static Socket s; + static ServerSocket ss; + static InetAddress addr; + static int port; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + testBindNull(); + + boolean error = true; + int value = 0; + addr = InetAddress.getLocalHost(); + + ss = Sockets.openRdmaServerSocket(); + s = Sockets.openRdmaSocket(); + addr = InetAddress.getLocalHost(); + ss.bind(new InetSocketAddress(addr, 0)); + + port = ss.getLocalPort(); + + Thread t = new Thread(new GetLocalAddress1()); + t.start(); + + Socket soc = ss.accept(); + + if(addr.equals(soc.getLocalAddress())) { + error = false; + } + if (error) + throw new RuntimeException("Socket.GetLocalAddress failed."); + soc.close(); + } + + public void run() { + try { + s.connect(new InetSocketAddress(addr, port)); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Socket.GetLocalAddress failed."); + } + } + + static void testBindNull() throws Exception { + try { + Socket soc = Sockets.openRdmaSocket(); + soc.bind(null); + if (!soc.isBound()) + throw new RuntimeException( + "should be bound after bind(null)"); + if (soc.getLocalPort() <= 0) + throw new RuntimeException( + "bind(null) failed, local port: " + soc.getLocalPort()); + if (soc.getLocalAddress() == null) + throw new RuntimeException( + "bind(null) failed, local address is null"); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Socket.GetLocalAddress failed."); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/InheritTimeout.java 2018-06-22 12:24:14.209671462 -0700 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Setting ServerSocket.setSoTimeout shouldn't cause + * the timeout to be inherited by accepted connections + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true InheritTimeout + */ + +import java.net.*; +import java.io.InputStream; +import jdk.net.*; + +public class InheritTimeout implements Runnable { + + static Socket s1; + static ServerSocket ss; + static InetAddress addr; + + class Reaper extends Thread { + Socket s; + int timeout; + + Reaper(Socket s, int timeout) { + this.s = s; + this.timeout = timeout; + } + + public void run() { + try { + Thread.currentThread().sleep(timeout); + s.close(); + } catch (Exception e) { + } + } + } + + class Client extends Thread { + public void run() { + InetSocketAddress isa = new InetSocketAddress(addr, ss.getLocalPort()); + try { + s1.connect(isa); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + InheritTimeout() throws Exception { + ss = Sockets.openRdmaServerSocket(); + ss.bind(new InetSocketAddress(addr, 0)); + + ss.setSoTimeout(1000); + + s1 = Sockets.openRdmaSocket(); + + Client c = new Client(); + c.start(); + + Socket s2 = ss.accept(); + + Reaper r = new Reaper(s2, 5000); + r.start(); + + boolean readTimedOut = false; + try { + s2.getInputStream().read(); + } catch (SocketTimeoutException te) { + readTimedOut = true; + } catch (SocketException e) { + if (!s2.isClosed()) { + throw e; + } + } + s1.close(); + ss.close(); + + if (readTimedOut) { + throw new Exception("Unexpected SocketTimeoutException throw!"); + } + } + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + new InheritTimeout(); + } + + public void run() { + InetSocketAddress isa = new InetSocketAddress(addr, ss.getLocalPort()); + try { + s1.connect(isa); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/ReadTimeout.java 2018-06-22 12:24:14.717671448 -0700 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary test timeout on a socket read + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm/timeout=15 -Djava.net.preferIPv4Stack=true ReadTimeout + */ + +import java.net.*; +import java.io.*; +import jdk.net.Sockets; + +public class ReadTimeout implements Runnable { + static Socket soc, soc1; + static ServerSocket srv; + static InetAddress sin; + static int port = 0; + static InputStream is; + static OutputStream os; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + int tout = 1000; + + sin = InetAddress.getLocalHost(); + srv = Sockets.openRdmaServerSocket(); + srv.bind(new InetSocketAddress(sin, port)); + port = srv.getLocalPort(); + + Thread t = new Thread(new ReadTimeout()); + t.start(); + + soc1 = srv.accept(); + soc.setSoTimeout(tout); + + try { + is = soc.getInputStream(); + os = soc1.getOutputStream(); + is.read(); + } catch(InterruptedIOException e) { + } finally { + soc.close(); + soc1.close(); + srv.close(); + } + } + + public void run() { + try { + soc = Sockets.openRdmaSocket(); + soc.connect(new InetSocketAddress(sin, port)); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/ShutdownBoth.java 2018-06-22 12:24:15.222671434 -0700 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Check that shutdownInput followed by shutdownOutput + * doesn't throw an exception. + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true ShutdownBoth + */ +import java.net.*; +import jdk.net.Sockets; + +public class ShutdownBoth implements Runnable { + static Socket s1, s2; + static ServerSocket ss; + static InetAddress addr; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + ss = Sockets.openRdmaServerSocket(); + addr = InetAddress.getLocalHost(); + ss.bind(new InetSocketAddress(addr, 0)); + + s1 = Sockets.openRdmaSocket(); + + Thread t = new Thread(new ShutdownBoth()); + t.start(); + + s2 = ss.accept(); + + try { + s1.shutdownInput(); + s1.shutdownOutput(); + } finally { + s1.close(); + s2.close(); + ss.close(); + } + } + public void run() { + try { + s1.connect(new InetSocketAddress(addr, ss.getLocalPort())); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/SoTimeout.java 2018-06-22 12:24:15.783671419 -0700 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Socket.setSoTimeout(T) can cause incorrect delay of T + * under green threads + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true SoTimeout + */ + +/* + * This program depends a bit on the particular behaviour of the green + * threads scheduler to produce the problem, but given that the underlying + * bug a green threads bug, I think that's OK. + */ + +import java.net.*; +import jdk.net.Sockets; + +public class SoTimeout implements Runnable { + static ServerSocket serverSocket; + static long timeWritten; + static InetAddress addr; + static int port; + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + addr = InetAddress.getLocalHost(); + + serverSocket = Sockets.openRdmaServerSocket(); + addr = InetAddress.getLocalHost(); + serverSocket.bind(new InetSocketAddress(addr, 0)); + + port = serverSocket.getLocalPort(); + + byte[] b = new byte[12]; + Thread t = new Thread(new SoTimeout()); + t.start(); + + Socket s = serverSocket.accept(); + serverSocket.close(); + + // set a 5 second timeout on the socket + s.setSoTimeout(5000); + + s.getInputStream().read(b, 0, b.length); + s.close(); + + long waited = System.currentTimeMillis() - timeWritten; + + // this sequence should complete fairly quickly and if it + // takes something resembling the the SoTimeout value then + // we are probably incorrectly blocking and not waking up + if (waited > 2000) { + throw new Exception("shouldn't take " + waited + " to complete"); + } + } + + public void run() { + try { + byte[] b = new byte[12]; + Socket s = Sockets.openRdmaSocket(); + s.connect(new InetSocketAddress(addr, port)); + + Thread.yield(); + timeWritten = System.currentTimeMillis(); + s.getOutputStream().write(b, 0, 12); + s.close(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/Streams.java 2018-06-22 12:24:16.351671404 -0700 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Spurious NPE from RdmaSocket.getIn/OutputStream + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Streams + */ + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.concurrent.Phaser; +import jdk.net.Sockets; + +// Racey test, will not always fail, but if it does then there is a problem. + +public class Streams { + static final int NUM_THREADS = 10; + static volatile boolean failed; + static ServerSocket ss; + static final Phaser startingGate = new Phaser(NUM_THREADS + 1); + static InetAddress ia; + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + InetAddress ia = InetAddress.getLocalHost(); + + try { + ss = Sockets.openRdmaServerSocket(); + ss.bind(new InetSocketAddress(ia, 0)); + runTest(OutputStreamGetter.class); + runTest(InputStreamGetter.class); + } catch (Exception e) { + e.printStackTrace(); + failed = true; + } + + if (failed) + throw new RuntimeException("Failed, check output"); + } + + static void runTest(Class klass) + throws Exception + { + final int port = ss.getLocalPort(); + Socket[] sockets = new Socket[NUM_THREADS]; + + for (int i = 0; i < NUM_THREADS; i++) { + sockets[i] = Sockets.openRdmaSocket(); + final Socket cs = sockets[i]; + Thread t = new Thread() { + public void run() { + try { + cs.connect(new InetSocketAddress(InetAddress.getLocalHost(), port)); + } catch (Exception e) { + e.printStackTrace(); + failed = true; + } + } + }; + t.start(); + Socket socket = ss.accept(); + cs.close(); + } + + Constructor ctr = klass.getConstructor(Socket.class); + + Thread[] threads = new Thread[NUM_THREADS]; + for (int i=0; i 0) + throw new RuntimeException("Failed: failcount = " + failCount); + + } + + public void run() { + try { + socket.connect(new InetSocketAddress(ia, ss.getLocalPort())); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + + static void test(Socket socket) throws IOException { + //Before Close + int socketPort = socket.getPort(); + InetAddress socketInetAddress = socket.getInetAddress(); + SocketAddress socketRemoteSocketAddress = socket.getRemoteSocketAddress(); + int socketLocalPort = socket.getLocalPort(); + + //After Close + socket.close(); + + if (socketPort != socket.getPort()) { + System.out.println("Socket.getPort failed"); + failCount++; + } + + if (!socket.getInetAddress().equals(socketInetAddress)) { + System.out.println("Socket.getInetAddress failed"); + failCount++; + } + + if (!socket.getRemoteSocketAddress().equals(socketRemoteSocketAddress)) { + System.out.println("Socket.getRemoteSocketAddresss failed"); + failCount++; + } + + if (socketLocalPort != socket.getLocalPort()) { + System.out.println("Socket.getLocalPort failed"); + failCount++; + } + + InetAddress anyAddr = null; + try { + anyAddr = InetAddress.getByAddress("",new byte[] {0,0,0,0}); + } catch (UnknownHostException uhe) { + } + + if (anyAddr != null && !socket.getLocalAddress().equals(anyAddr)) { + System.out.println("Socket.getLocalAddress failed"); + failCount++; + } + + InetSocketAddress addr = new InetSocketAddress(socket.getLocalPort()); + if (!socket.getLocalSocketAddress().equals(addr)) { + System.out.println("Socket.getLocalSocketAddress failed"); + failCount++; + } + + if (!socket.isConnected()) { + System.out.println("Socket.isConnected failed"); + failCount++; + } + + if (!socket.isBound()) { + System.out.println("Socket.isBound failed"); + failCount++; + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/TestClose.java 2018-06-22 12:24:17.359671377 -0700 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for socket channels + * @summary This tests whether it's possible to get some informations + * out of a closed socket. This is for backward compatibility + * purposes. + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true TestClose + */ +import java.net.*; +import jdk.net.Sockets; + +public class TestClose implements Runnable { + + static ServerSocket ss; + static Socket s; + static InetAddress ia; + static int serverport; + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + InetAddress ad1, ad2; + int port1, port2; + + ia = InetAddress.getLocalHost(); + ss = Sockets.openRdmaServerSocket(); + ss.bind(new InetSocketAddress(ia, 0)); + serverport = ss.getLocalPort(); + + s = Sockets.openRdmaSocket(); + Thread t = new Thread(new TestClose()); + t.start(); + + Socket s2 = ss.accept(); + s.close(); + s2.close(); + ss.close(); + + ad1 = ss.getInetAddress(); + if (ad1 == null) + throw new RuntimeException("ServerSocket.getInetAddress() returned null"); + port1 = ss.getLocalPort(); + if (port1 != serverport) + throw new RuntimeException("ServerSocket.getLocalPort() returned the wrong value"); + ad2 = s.getInetAddress(); + if (ad2 == null) + throw new RuntimeException("Socket.getInetAddress() returned null"); + port2 = s.getPort(); + if (port2 != serverport) + throw new RuntimeException("Socket.getPort() returned wrong value"); + ad2 = s.getLocalAddress(); + if (ad2 == null) + throw new RuntimeException("Socket.getLocalAddress() returned null"); + port2 = s.getLocalPort(); + if (port2 == -1) + throw new RuntimeException("Socket.getLocalPort returned -1"); + } + + public void run() { + try { + s.connect(new InetSocketAddress(ia, serverport)); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/TestTcpNoDelay.java 2018-06-22 12:24:17.862671363 -0700 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary RDMA Socket setTcpNoDelay & setKeepAlive working incorrectly + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true TestTcpNoDelay + */ + +import java.net.*; +import java.io.IOException; +import jdk.net.Sockets; + +public class TestTcpNoDelay +{ + public static void main(String[] args) { + if (!RsocketTest.isRsocketAvailable()) + return; + + try { + Socket socket = Sockets.openRdmaSocket(); + boolean on = socket.getTcpNoDelay(); + System.out.println("Get TCP_NODELAY = " + on); + + boolean opposite = on ? false: true; + System.out.println("Set TCP_NODELAY to " + opposite); + socket.setTcpNoDelay(opposite); + + boolean noDelay = socket.getTcpNoDelay(); + System.out.println("Get TCP_NODELAY = " + noDelay); + + if (noDelay != opposite) + throw new RuntimeException("setTcpNoDelay no working as expected"); + + } catch (IOException e){ + e.printStackTrace(); + } + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/Socket/Timeout.java 2018-06-22 12:24:18.362671350 -0700 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test if timeout hanges + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm/timeout=15 -Djava.net.preferIPv4Stack=true Timeout + */ +import java.net.*; +import java.io.*; +import jdk.net.Sockets; + +public class Timeout { + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + boolean success = false; + ServerSocket sock = Sockets.openRdmaServerSocket(); + sock.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0)); + try { + sock.setSoTimeout(2); + sock.accept(); + } catch (InterruptedIOException e) { + success = true; + } finally { + sock.close(); + } + if (!success) + throw new RuntimeException("Socket timeout failure."); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/AdaptSocket.java 2018-06-22 12:24:18.870671336 -0700 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for socket-channel adaptors + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true AdaptSocket + */ + +import java.io.*; +import java.net.*; +import java.nio.channels.*; +import java.util.Arrays; +import jdk.net.Sockets; + +public class AdaptSocket { + + static final java.io.PrintStream out = System.out; + + static void test(TestServers.AbstractServer server, + int timeout, + boolean shouldTimeout) + throws Exception + { + out.println(); + + InetSocketAddress isa = new InetSocketAddress(server.getAddress(), server.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + Socket so = sc.socket(); + out.println("opened: " + so); + out.println(" " + sc); + + so.setTcpNoDelay(true); + so.setKeepAlive(true); + so.setReceiveBufferSize(512); + so.setSendBufferSize(512); + + if (timeout == 0) + so.connect(isa); + else { + try { + so.connect(isa, timeout); + } catch (SocketTimeoutException x) { + if (shouldTimeout) { + out.println("Connection timed out, as expected"); + return; + } else { + throw x; + } + } + } + out.println("connected: " + so); + out.println(" " + sc); + byte[] bb = new byte[100]; + int n = so.getInputStream().read(bb); + String s = new String(bb, 0, n - 2, "US-ASCII"); + out.println(isa + " says: \"" + s + "\""); + so.shutdownInput(); + out.println("ishut: " + sc); + so.shutdownOutput(); + out.println("oshut: " + sc); + so.close(); + out.println("closed: " + so); + out.println(" " + sc); + } + + static String dataString = "foo\r\n"; + + static void testRead(Socket so, boolean shouldTimeout) + throws Exception + { + String data = "foo\r\n"; + so.getOutputStream().write(dataString.getBytes("US-ASCII")); + InputStream is = so.getInputStream(); + try { + byte[] b = new byte[100]; + int n = is.read(b); + if (shouldTimeout) { + throw new Exception("Should time out, but not, data: " + Arrays.toString(b)); + } + if (n != 5) { + throw new Exception("Incorrect number of bytes read: " + n); + } + if (!dataString.equals(new String(b, 0, n, "US-ASCII"))) { + throw new Exception("Incorrect data read: " + n); + } + } catch (SocketTimeoutException x) { + if (shouldTimeout) { + out.println("Read timed out, as expected"); + return; + } + throw x; + } + } + + static void testRead(TestServers.EchoServer echoServer, + int timeout, + boolean shouldTimeout) + throws Exception + { + out.println(); + + InetSocketAddress isa + = new InetSocketAddress(echoServer.getAddress(), + echoServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.connect(isa); + Socket so = sc.socket(); + out.println("connected: " + so); + out.println(" " + sc); + + if (timeout > 0) + so.setSoTimeout(timeout); + out.println("timeout: " + so.getSoTimeout()); + + testRead(so, shouldTimeout); + for (int i = 0; i < 4; i++) { + out.println("loop: " + i); + testRead(so, shouldTimeout); + } + + sc.close(); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer dayTimeServer + = TestServers.DayTimeServer.startNewServer()) { + test(dayTimeServer, 0, false); + test(dayTimeServer, 1000, false); + } + + try (TestServers.DayTimeServer lingerDayTimeServer + = TestServers.DayTimeServer.startNewServer(100)) { + // this test no longer really test the connection timeout + // since there is no way to prevent the server from eagerly + // accepting connection... + test(lingerDayTimeServer, 10, true); + } + + try (TestServers.EchoServer echoServer + = TestServers.EchoServer.startNewServer()) { + testRead(echoServer, 0, false); + testRead(echoServer, 8000, false); + } + + try (TestServers.NoResponseServer noResponseServer + = TestServers.NoResponseServer.startNewServer()) { + testRead(noResponseServer, 10, true); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/AsyncCloseChannel.java 2018-06-22 12:24:19.376671322 -0700 @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Check no data is written to wrong socket channel during async closing. + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true AsyncCloseChannel + */ + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import jdk.net.Sockets; + +public class AsyncCloseChannel { + static volatile boolean failed = false; + static volatile boolean keepGoing = true; + static int maxAcceptCount = 20; + static volatile int acceptCount = 0; + static InetAddress host; + static int sensorPort; + static int targetPort; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + host = InetAddress.getLocalHost(); + Thread ss = new SensorServer(); ss.start(); + Thread ts = new TargetServer(); ts.start(); + + sensorPort = ((ServerThread)ss).server.getLocalPort(); + targetPort = ((ServerThread)ts).server.getLocalPort(); + + Thread sc = new SensorClient(); sc.start(); + Thread tc = new TargetClient(); tc.start(); + + while(acceptCount < maxAcceptCount && !failed) { + Thread.sleep(10); + } + keepGoing = false; + try { + ss.interrupt(); + ts.interrupt(); + sc.interrupt(); + tc.interrupt(); + } catch (Exception e) {} + if (failed) + throw new RuntimeException("AsyncCloseChannel2 failed after <" + + acceptCount + "> times of accept!"); + } + + static class SensorServer extends ServerThread { + public void runEx() throws Exception { + while(keepGoing) { + try { + final Socket s = server.accept(); + new Thread() { + public void run() { + try { + int c = s.getInputStream().read(); + if(c != -1) { + // No data is ever written to the peer's socket! + System.err.println("Oops: read a character: " + + (char) c); + failed = true; + } + } catch (IOException ex) { + ex.printStackTrace(); + } finally { + closeIt(s); + } + } + }.start(); + } catch (IOException ex) { + System.err.println("Exception on sensor server " + ex.getMessage()); + } + } + } + } + + static class TargetServer extends ServerThread { + public void runEx() throws Exception { + while (keepGoing) { + try { + final Socket s = server.accept(); + acceptCount++; + new Thread() { + public void run() { + boolean empty = true; + try { + for(;;) { + int c = s.getInputStream().read(); + if(c == -1) { + if(!empty) + break; + } + empty = false; + } + } catch (IOException ex) { + ex.printStackTrace(); + } finally { + closeIt(s); + } + } + }.start(); + } catch (IOException ex) { + System.err.println("Exception on target server " + ex.getMessage()); + } + } + } + } + + static class SensorClient extends Thread { + private static boolean wake; + private static SensorClient theClient; + public void run() { + while (keepGoing) { + Socket s = null; + try { + s = Sockets.openRdmaSocket(); + synchronized(this) { + while(!wake && keepGoing) { + try { + wait(); + } catch (InterruptedException ex) { } + } + wake = false; + } + s.connect(new InetSocketAddress(host, sensorPort)); + try { + Thread.sleep(10); + } catch (InterruptedException ex) { } + } catch (IOException ex) { + System.err.println("Exception on sensor client " + ex.getMessage()); + } finally { + if(s != null) { + try { + s.close(); + } catch(IOException ex) { ex.printStackTrace();} + } + } + } + } + + public SensorClient() { + theClient = this; + } + + public static void wakeMe() { + synchronized(theClient) { + wake = true; + theClient.notify(); + } + } + } + + static class TargetClient extends Thread { + volatile boolean ready = false; + public void run() { + while(keepGoing) { + try { + final SocketChannel s = Sockets.openRdmaSocketChannel(); + s.connect(new InetSocketAddress(host, targetPort)); + s.finishConnect(); + ready = false; + Thread t = new Thread() { + public void run() { + ByteBuffer b = ByteBuffer.allocate(1); + try { + for(;;) { + b.clear(); + b.put((byte) 'A'); + b.flip(); + s.write(b); + ready = true; + } + } catch (IOException ex) { + if(!(ex instanceof ClosedChannelException)) + System.err.println("Exception in target client child " + + ex.toString()); + } + } + }; + t.start(); + while(!ready && keepGoing) { + try { + Thread.sleep(10); + } catch (InterruptedException ex) {} + } + s.close(); + SensorClient.wakeMe(); + t.join(); + } catch (IOException ex) { + System.err.println("Exception in target client parent " + + ex.getMessage()); + } catch (InterruptedException ex) {} + } + } + } + + static abstract class ServerThread extends Thread { + ServerSocket server; + public ServerThread() { + super(); + try { + server = Sockets.openRdmaServerSocket(); + server.bind(new InetSocketAddress(host, 0)); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + public void interrupt() { + super.interrupt(); + if (server != null) { + try { + server.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + public void run() { + try { + runEx(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + abstract void runEx() throws Exception; + } + + public static void closeIt(Socket s) { + try { + if(s != null) + s.close(); + } catch (IOException ex) { } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/Basic.java 2018-06-22 12:24:19.927671308 -0700 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for socket channels + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Basic + */ + +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.charset.*; +import jdk.net.Sockets; + +public class Basic { + + static java.io.PrintStream out = System.out; + + static void test(TestServers.DayTimeServer daytimeServer) throws Exception { + InetSocketAddress isa + = new InetSocketAddress(daytimeServer.getAddress(), + daytimeServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + out.println("opened: " + sc); + sc.connect(isa); + out.println("connected: " + sc); + ByteBuffer bb = ByteBuffer.allocateDirect(100); + int n = sc.read(bb); + bb.position(bb.position() - 2); // Drop CRLF + bb.flip(); + CharBuffer cb = Charset.forName("US-ASCII").newDecoder().decode(bb); + out.println(isa + " says: \"" + cb + "\""); + sc.socket().shutdownInput(); + out.println("ishut: " + sc); + sc.socket().shutdownOutput(); + out.println("oshut: " + sc); + sc.close(); + out.println("closed: " + sc); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer dayTimeServer + = TestServers.DayTimeServer.startNewServer(100)) { + test(dayTimeServer); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/BasicSocketChannelTest.java 2018-06-22 12:24:20.435671294 -0700 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8195160 + * @summary Test for basic functionality for RdmaSocketChannel + * and RdmaServerSocketChannel + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true BasicSocketChannelTest + */ + +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.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import jdk.net.Sockets; + +public class BasicSocketChannelTest implements Runnable { + static ServerSocketChannel ssc; + static SocketChannel sc1, sc2; + static InetAddress iaddr; + static int port = 0; + static String message = "This is a message!"; + static int length = -1; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + iaddr = InetAddress.getLocalHost(); + length = message.length(); + String result; + + //test SocketChannel and ServerSocketChannel + try { + ssc = Sockets.openRdmaServerSocketChannel(); + sc1 = Sockets.openRdmaSocketChannel(); + + ssc.bind(new InetSocketAddress(iaddr, port)); + + Thread t = new Thread(new BasicSocketChannelTest(), "Channel"); + t.start(); + + sc2 = ssc.accept(); + ByteBuffer output = ByteBuffer.allocate(length); + int outputNum = 0; + while (outputNum < length) { + outputNum += sc2.read(output); + } + + result = new String(output.array()); + if(!result.equals(message)) + throw new RuntimeException("Test Failed!"); + sc2.shutdownInput(); + sc2.shutdownOutput(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } finally { + ssc.close(); + sc1.close(); + sc2.close(); + } + + //test SocketChannel.socket() and ServerSocketChannel.socket() + try { + ssc = Sockets.openRdmaServerSocketChannel(); + sc1 = Sockets.openRdmaSocketChannel(); + + ssc.socket().bind(new InetSocketAddress(iaddr, port)); + + Thread t = new Thread(new BasicSocketChannelTest(), "Socket"); + t.start(); + + sc2 = ssc.accept(); + Socket conn = sc2.socket(); + InputStream is = conn.getInputStream(); + + int num = 0; + byte[] buf = new byte[length]; + while (num < length) { + int l = is.read(buf); + num += l; + } + + result = new String(buf); + if(!result.equals(message)) + throw new RuntimeException("Test Failed!"); + conn.shutdownInput(); + conn.shutdownOutput(); + if (!conn.isInputShutdown() || !conn.isOutputShutdown()) + throw new RuntimeException("Test Failed!"); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } finally { + ssc.close(); + sc1.close(); + sc2.close(); + } + } + + public void run() { + int port = ssc.socket().getLocalPort(); + byte[] arr = new byte[length]; + arr = message.getBytes(); + + try { + if (Thread.currentThread().getName().startsWith("Channel")) { + sc1.connect(new InetSocketAddress(iaddr, port)); + if (!sc1.isConnected()) + throw new RuntimeException("Test Failed!"); + ByteBuffer input = ByteBuffer.allocate(length); + input.put(arr); + input.flip(); + int inputNum = 0; + while (inputNum < length) { + inputNum += sc1.write(input); + } + sc1.shutdownInput(); + sc1.shutdownOutput(); + } else if (Thread.currentThread().getName().startsWith("Socket")) { + Socket client = sc1.socket(); + client.connect(new InetSocketAddress(iaddr, port)); + if (!client.isConnected()) + throw new RuntimeException("Test Failed!"); + OutputStream os = client.getOutputStream(); + os.write(arr); + client.shutdownInput(); + client.shutdownOutput(); + if (!client.isInputShutdown() || !client.isOutputShutdown()) + throw new RuntimeException("Test Failed!"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed!"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/Close.java 2018-06-22 12:24:21.024671278 -0700 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for closing socket channels + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Close + */ +import java.io.IOException; +import java.net.*; +import java.nio.channels.*; +import jdk.net.Sockets; + +public class Close { + + static SelectionKey open() throws IOException { + SocketChannel sc = Sockets.openRdmaSocketChannel(); + Selector sel = Sockets.openRdmaSelector(); + sc.configureBlocking(false); + return sc.register(sel, SelectionKey.OP_READ); + } + + static void check(SelectionKey sk) throws IOException { + if (sk.isValid()) + throw new RuntimeException("Key still valid"); + if (sk.channel().isOpen()) + throw new RuntimeException("Channel still open"); + } + + static void testSocketClose() throws IOException { + SelectionKey sk = open(); + check(sk); + } + + static void testChannelClose() throws IOException { + SelectionKey sk = open(); + try { + sk.channel().close(); + check(sk); + } finally { + sk.selector().close(); + } + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + testChannelClose(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/CloseDuringWrite.java 2018-06-22 12:24:21.523671265 -0700 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test asynchronous close during a blocking write + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true CloseDuringWrite + * @key randomness + */ +import java.io.Closeable; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.concurrent.*; +import java.util.Random; +import jdk.net.Sockets; + +public class CloseDuringWrite { + + static final Random rand = new Random(); + static ServerSocketChannel ssc; + static SocketChannel source; + static SocketAddress sa; + + /** + * A task that closes a Closeable + */ + static class Closer implements Callable { + final Closeable c; + Closer(Closeable c) { + this.c = c; + } + public Void call() throws IOException { + c.close(); + return null; + } + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor(); + try { + try { + ssc = Sockets.openRdmaServerSocketChannel(); + InetAddress lh = InetAddress.getLocalHost(); + ssc.bind(new InetSocketAddress(lh, 0)); + int port = ssc.socket().getLocalPort(); + sa = new InetSocketAddress(lh, port); + + ByteBuffer bb = ByteBuffer.allocate(2 * 1024 * 1024); + + for (int i = 0; i < 20; i++) { + try { + source = Sockets.openRdmaSocketChannel(); + Runnable runnable = new Runnable() { + @Override + public void run() { + try { + source.connect(sa); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + }; + + Thread t = new Thread(runnable); + t.start(); + SocketChannel sink = ssc.accept(); + // schedule channel to be closed + Closer c = new Closer(source); + int when = 1000 + rand.nextInt(2000); + Future result = pool.schedule(c, when, TimeUnit.MILLISECONDS); + + // the write should either succeed or else throw a + // ClosedChannelException (more likely an + // AsynchronousCloseException) + try { + for (;;) { + int limit = rand.nextInt(bb.capacity()); + bb.position(0); + bb.limit(limit); + int n = source.write(bb); + System.out.format("wrote %d, expected %d%n", n, limit); + } + } catch (ClosedChannelException expected) { + System.out.println(expected + " (expected)"); + } finally { + result.get(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } finally { + pool.shutdown(); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/Connect.java 2018-06-22 12:24:22.087671249 -0700 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for socket channels + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Connect + */ + +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import jdk.net.Sockets; + +public class Connect { + + private static final long INCREMENTAL_DELAY = 30L * 1000L; + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.EchoServer echoServer + = TestServers.EchoServer.startNewServer(1000)) { + test1(echoServer); + } + try { + test1(TestServers.RefusingServer.newRefusingServer()); + throw new Exception("Refused connection throws no exception"); + } catch (ConnectException ce) { + // Correct result + } + } + + static void test1(TestServers.AbstractServer server) throws Exception { + Selector selector; + SocketChannel sc; + SelectionKey sk; + InetSocketAddress isa = new InetSocketAddress( + server.getAddress(), server.getPort()); + sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + + selector = Sockets.openRdmaSelector(); + sk = sc.register(selector, SelectionKey.OP_CONNECT); + if (sc.connect(isa)) { + System.err.println("Connected immediately!"); + sc.close(); + selector.close(); + return; + } else { + ByteBuffer buf = ByteBuffer.allocateDirect(100); + buf.asCharBuffer().put(new String( + "The quick brown fox jumped over the lazy dog." + ).toCharArray()); + buf.flip(); + long startTime = System.currentTimeMillis(); + while(true) { + selector.select(INCREMENTAL_DELAY); + Set selectedKeys = selector.selectedKeys(); + + if(selectedKeys.isEmpty()) { + System.err.println("Elapsed time without response: " + + (System.currentTimeMillis() - + startTime) / 1000L + " seconds."); + } + else if(!selectedKeys.contains(sk)) + { + System.err.println("Got wrong event about selection key."); + } else { + System.err.println("Got event for our selection key."); + if(sk.isConnectable()) { + if(sc.finishConnect()) { + if(sc.isConnected()) { + System.err.println("Successful connect."); + sk.interestOps(SelectionKey.OP_WRITE); + sc.write(buf); + } else { + System.err.println( + "Finish connect completed incorrectly."); + } + } else { + System.err.println( + "key incorrectly indicated socket channel connectable."); + } + } + if(sk.isWritable() && (buf.remaining() > 0)) { + sc.write(buf); + } + if(buf.remaining() == 0) { + System.err.println( + "SUCCESS! buffer contents were sent."); + sc.close(); + selector.close(); + return; + } + } + selectedKeys.clear(); + } + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/ConnectState.java 2018-06-22 12:24:22.621671235 -0700 @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test socket-channel connection-state transitions + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true ConnectState + */ + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import jdk.net.Sockets; + +public class ConnectState { + + static PrintStream log = System.err; + + static InetSocketAddress remote; + + final static int ST_UNCONNECTED = 0; + final static int ST_PENDING = 1; + final static int ST_CONNECTED = 2; + final static int ST_CLOSED = 3; + final static int ST_PENDING_OR_CONNECTED = 4; + // NO exceptions expected + final static Collection> NONE = Collections.emptySet(); + + // make a set of expected exception. + static Collection> expectedExceptions(Class... expected) { + final Collection> exceptions; + if (expected.length == 0) { + exceptions = NONE; + } else if (expected.length == 1) { + assert expected[0] != null; + exceptions = Collections.>singleton(expected[0]); + } else { + exceptions = new HashSet<>(Arrays.asList(expected)); + } + return exceptions; + } + + static abstract class Test { + + abstract String go(SocketChannel sc) throws Exception; + + static void check(boolean test, String desc) throws Exception { + if (!test) + throw new Exception("Incorrect state: " + desc); + } + + static void check(SocketChannel sc, int state) throws Exception { + switch (state) { + case ST_UNCONNECTED: + check(!sc.isConnected(), "!isConnected"); + check(!sc.isConnectionPending(), "!isConnectionPending"); + check(sc.isOpen(), "isOpen"); + break; + case ST_PENDING: + check(!sc.isConnected(), "!isConnected"); + check(sc.isConnectionPending(), "isConnectionPending"); + check(sc.isOpen(), "isOpen"); + break; + case ST_CONNECTED: + check(sc.isConnected(), "isConnected"); + check(!sc.isConnectionPending(), "!isConnectionPending"); + check(sc.isOpen(), "isOpen"); + break; + case ST_CLOSED: + check(sc.isConnected(), "isConnected"); + check(!sc.isConnectionPending(), "!isConnectionPending"); + check(sc.isOpen(), "isOpen"); + break; + case ST_PENDING_OR_CONNECTED: + check(sc.isConnected() || sc.isConnectionPending(), + "isConnected || isConnectionPending"); + check(sc.isOpen(), "isOpen"); + break; + } + } + + Test(String name, Class exception, int state) throws Exception { + this(name, expectedExceptions(exception), state); + } + + // On some architecture we may need to accept several exceptions. + // For instance on Solaris, when using a server colocated on the + // machine we cannot guarantee that we will get a + // ConnectionPendingException when connecting twice on the same + // non-blocking socket. We may instead get a an + // AlreadyConnectedException, which is also valid: it simply means + // that the first connection has been immediately accepted. + Test(String name, Collection> exceptions, int state) + throws Exception { + SocketChannel sc = Sockets.openRdmaSocketChannel(); + String note; + try { + try { + note = go(sc); + } catch (Exception x) { + Class expectedExceptionClass = null; + for (Class exception : exceptions) { + if (exception.isInstance(x)) { + log.println(name + ": As expected: " + + x); + expectedExceptionClass = exception; + check(sc, state); + break; + } + } + if (expectedExceptionClass == null + && !exceptions.isEmpty()) { + // we had an exception, but it's not of the set of + // exceptions we expected. + throw new Exception(name + + ": Incorrect exception", + x); + } else if (exceptions.isEmpty()) { + // we didn't expect any exception + throw new Exception(name + + ": Unexpected exception", + x); + } + // if we reach here, we have our expected exception + assert expectedExceptionClass != null; + return; + } + if (!exceptions.isEmpty()) { + throw new Exception(name + + ": Expected exception not thrown: " + + exceptions.iterator().next()); + } + check(sc, state); + log.println(name + ": Returned normally" + + ((note != null) ? ": " + note : "")); + } finally { + if (sc.isOpen()) + sc.close(); + } + } + + } + + static void tests() throws Exception { + log.println(remote); + + new Test("Read unconnected", NotYetConnectedException.class, + ST_UNCONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + ByteBuffer b = ByteBuffer.allocateDirect(1024); + sc.read(b); + return null; + }}; + + new Test("Write unconnected", NotYetConnectedException.class, + ST_UNCONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + ByteBuffer b = ByteBuffer.allocateDirect(1024); + sc.write(b); + return null; + }}; + + new Test("Simple connect", NONE, ST_CONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + sc.connect(remote); + return null; + }}; + + new Test("Simple connect & finish", NONE, ST_CONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + sc.connect(remote); + if (!sc.finishConnect()) + throw new Exception("finishConnect returned false"); + return null; + }}; + + new Test("Double connect", + AlreadyConnectedException.class, ST_CONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + sc.connect(remote); + sc.connect(remote); + return null; + }}; + + new Test("Finish w/o start", + NoConnectionPendingException.class, ST_UNCONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + sc.finishConnect(); + return null; + }}; + + // Note: using our local EchoServer rather than echo on a distant + // host - we see that Tries to finish = 0 (instead of ~ 18). + new Test("NB simple connect", NONE, ST_CONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + sc.configureBlocking(false); + sc.connect(remote); + int n = 0; + while (!sc.finishConnect()) { + Thread.sleep(10); + n++; + } + sc.finishConnect(); // Check redundant invocation + return ("Tries to finish = " + n); + }}; + + // Note: using our local EchoServer rather than echo on a distant + // host - we cannot guarantee that this test will get a + // a ConnectionPendingException: it may get an + // AlreadyConnectedException, so we should allow for both. + new Test("NB double connect", + expectedExceptions(ConnectionPendingException.class, + AlreadyConnectedException.class), + ST_PENDING_OR_CONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + sc.configureBlocking(false); + sc.connect(remote); + sc.connect(remote); + return null; + }}; + + new Test("NB finish w/o start", + NoConnectionPendingException.class, ST_UNCONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + sc.configureBlocking(false); + sc.finishConnect(); + return null; + }}; + + new Test("NB connect, B finish", NONE, ST_CONNECTED) { + @Override + String go(SocketChannel sc) throws Exception { + sc.configureBlocking(false); + sc.connect(remote); + sc.configureBlocking(true); + sc.finishConnect(); + return null; + }}; + + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.EchoServer echoServer + = TestServers.EchoServer.startNewServer(500)) { + remote = new InetSocketAddress(echoServer.getAddress(), + echoServer.getPort()); + tests(); + } + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/FinishConnect.java 2018-06-22 12:24:23.123671222 -0700 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test RDMA SocketChannel.finishConnect + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true FinishConnect + */ + +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.channels.spi.SelectorProvider; +import java.nio.charset.*; +import java.util.*; +import jdk.net.Sockets; + +public class FinishConnect { + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer dayTimeServer + = TestServers.DayTimeServer.startNewServer(100)) { + test1(dayTimeServer, true, true); + test1(dayTimeServer, true, false); + test1(dayTimeServer, false, true); + test1(dayTimeServer, false, false); + test2(dayTimeServer); + } + } + + static void test1(TestServers.DayTimeServer daytimeServer, + boolean select, + boolean setBlocking) + throws Exception + { + InetSocketAddress isa + = new InetSocketAddress(daytimeServer.getAddress(), + daytimeServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + boolean connected = sc.connect(isa); + int attempts = 0; + + try { + sc.connect(isa); + throw new RuntimeException("Allowed another connect call"); + } catch (IllegalStateException ise) { + // Correct behavior + } + + if (setBlocking) + sc.configureBlocking(true); + + if (!connected && select && !setBlocking) { + Selector selector = Sockets.openRdmaSelector(); + sc.register(selector, SelectionKey.OP_CONNECT); + while (!connected) { + int keysAdded = selector.select(100); + if (keysAdded > 0) { + Set readyKeys = selector.selectedKeys(); + Iterator i = readyKeys.iterator(); + while (i.hasNext()) { + SelectionKey sk = (SelectionKey)i.next(); + SocketChannel nextReady = + (SocketChannel)sk.channel(); + connected = sc.finishConnect(); + } + } + } + selector.close(); + } + + while (!connected) { + if (attempts++ > 30) + throw new RuntimeException("Failed to connect"); + Thread.sleep(100); + connected = sc.finishConnect(); + } + + ByteBuffer bb = ByteBuffer.allocateDirect(100); + int bytesRead = 0; + int totalRead = 0; + while (totalRead < 20) { + bytesRead = sc.read(bb); + if (bytesRead > 0) + totalRead += bytesRead; + if (bytesRead < 0) + throw new RuntimeException("Message shorter than expected"); + } + bb.position(bb.position() - 2); // Drop CRLF + bb.flip(); + CharBuffer cb = Charset.forName("US-ASCII").newDecoder().decode(bb); + System.err.println(isa + " says: \"" + cb + "\""); + sc.close(); + } + + static void test2(TestServers.DayTimeServer daytimeServer) throws Exception { + InetSocketAddress isa + = new InetSocketAddress(daytimeServer.getAddress(), + daytimeServer.getPort()); + boolean done = false; + int globalAttempts = 0; + int connectSuccess = 0; + while (!done) { + // When using a local daytime server it is not always possible + // to get a pending connection, as sc.connect(isa) may always + // return true. + // So we're going to throw the exception only if there was + // at least 1 case where we did not manage to connect. + if (globalAttempts++ > 50) { + if (globalAttempts == connectSuccess + 1) { + System.out.println("Can't fully test on " + + System.getProperty("os.name")); + break; + } + throw new RuntimeException("Failed to connect"); + } + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.configureBlocking(false); + boolean connected = sc.connect(isa); + int localAttempts = 0; + while (!connected) { + if (localAttempts++ > 500) + throw new RuntimeException("Failed to connect"); + connected = sc.finishConnect(); + if (connected) { + done = true; + break; + } + Thread.sleep(10); + } + if (connected) { + connectSuccess++; + } + sc.close(); + } + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/Shutdown.java 2018-06-22 12:24:23.626671208 -0700 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Unit test for shutdown, shutdownInput and isInputShutdown + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Connect + */ + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import jdk.net.Sockets; + +public class Shutdown { + + /** + * Accept a connection, and close it immediately causing a hard reset. + */ + static void acceptAndReset(ServerSocketChannel ssc) throws IOException { + SocketChannel peer = ssc.accept(); + try { + peer.configureBlocking(false); + peer.write(ByteBuffer.wrap(new byte[128*1024])); + } finally { + peer.close(); + } + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + InetAddress lh = InetAddress.getLocalHost(); + + ServerSocketChannel ssc = Sockets.openRdmaServerSocketChannel() + .bind(new InetSocketAddress(lh, 0)); + try { + int port = ((InetSocketAddress)(ssc.getLocalAddress())).getPort(); + SocketAddress remote = new InetSocketAddress(lh, port); + + // Test SocketChannel shutdownXXX + SocketChannel sc; + sc = Sockets.openRdmaSocketChannel(); + sc.connect(remote); + try { + acceptAndReset(ssc); + sc.shutdownInput(); + sc.shutdownOutput(); + } catch (Exception e) { + throw new RuntimeException("Test Failed"); + } finally { + sc.close(); + } + + // Test Socket adapter shutdownXXX and isShutdownInput + sc = Sockets.openRdmaSocketChannel(); + sc.connect(remote); + + try { + acceptAndReset(ssc); + boolean before = sc.socket().isInputShutdown(); + sc.socket().shutdownInput(); + boolean after = sc.socket().isInputShutdown(); + if (before || !after) + throw new RuntimeException("Before and after test failed"); + sc.socket().shutdownOutput(); + } finally { + sc.close(); + } + } finally { + ssc.close(); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/Stream.java 2018-06-22 12:24:24.179671193 -0700 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test result of read on stream from nonblocking channel + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.Utils TestServers + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true Stream + */ + +import java.io.*; +import java.net.*; +import java.nio.channels.*; +import jdk.net.Sockets; + +public class Stream { + + static void test(TestServers.DayTimeServer daytimeServer) throws Exception { + InetSocketAddress isa + = new InetSocketAddress(daytimeServer.getAddress(), + daytimeServer.getPort()); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.connect(isa); + sc.configureBlocking(false); + InputStream is = sc.socket().getInputStream(); + byte b[] = new byte[10]; + try { + int n = is.read(b); + throw new RuntimeException("Exception expected; none thrown"); + } catch (IllegalBlockingModeException e) { + // expected result + } + sc.close(); + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + try (TestServers.DayTimeServer dayTimeServer + = TestServers.DayTimeServer.startNewServer(100)) { + test(dayTimeServer); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/UnboundSocketTests.java 2018-06-22 12:24:24.689671179 -0700 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Check getXXX methods for local/remote port/address/socketaddress + * match socket spec for unbound case + * @requires (os.family == "linux") + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true UnboundSocketTests + */ +import java.net.*; +import java.nio.channels.*; +import jdk.net.Sockets; + +public class UnboundSocketTests { + + static int failures = 0; + + static void check(String msg, Object actual, Object expected) { + System.out.format("%s expected: %s, actual: %s", msg, expected, actual); + if (actual == expected) { + System.out.println(" [PASS]"); + } else { + System.out.println(" [FAIL]"); + failures++; + } + } + + static void checkIsAnyLocalAddress(String msg, InetAddress actual) { + System.out.format("%s actual: %s", msg, actual); + if (actual.isAnyLocalAddress()) { + System.out.println(" [PASS]"); + } else { + System.out.println(" [FAIL]"); + failures++; + } + } + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + System.out.println("\n-- SocketChannel --"); + + SocketChannel sc = Sockets.openRdmaSocketChannel(); + try { + check("getLocalPort()", sc.socket().getLocalPort(), -1); + checkIsAnyLocalAddress("getLocalAddress()", + sc.socket().getLocalAddress()); + check("getLocalSocketAddress()", sc.socket().getLocalSocketAddress(), null); + + check("getPort()", sc.socket().getPort(), 0); + check("getInetAddress()", sc.socket().getInetAddress(), null); + check("getRemoteSocketAddress()", sc.socket().getRemoteSocketAddress(), null); + } finally { + sc.close(); + } + + System.out.println("\n-- ServerSocketChannel --"); + + ServerSocketChannel ssc = Sockets.openRdmaServerSocketChannel(); + try { + check("getLocalPort()", ssc.socket().getLocalPort(), -1); + check("getInetAddress()", ssc.socket().getInetAddress(), null); + check("getLocalSocketAddress()", ssc.socket().getLocalSocketAddress(), null); + } finally { + ssc.close(); + } + + if (failures > 0) { + throw new RuntimeException(failures + " sub-tests(s) failed."); + } + + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketChannel/VectorIO.java 2018-06-22 12:24:25.187671166 -0700 @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8195160 + * @summary Test socketchannel vector IO (use -Dseed=X to set PRNG seed) + * @requires (os.family == "linux") + * @library .. /test/lib + * @build jdk.test.lib.RandomFactory + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true VectorIO + * @key randomness + */ +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import jdk.test.lib.RandomFactory; +import jdk.net.Sockets; + +public class VectorIO { + + private static Random generator = RandomFactory.getRandom(); + + static int testSize; + + // whether to use the write/read variant with a length parameter + static boolean setLength; + + public static void main(String[] args) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + testSize = 1; + setLength = false; + runTest(); + for(int i = 15; i < 18; i++) { + testSize = i; + setLength = !setLength; + runTest(); + } + } + + static void runTest() throws Exception { + System.err.println("Length " + testSize); + Server sv = new Server(testSize); + sv.start(); + bufferTest(sv.port()); + if (sv.finish(8000) == 0) + throw new Exception("Failed: Length = " + testSize); + } + + static void bufferTest(int port) throws Exception { + ByteBuffer[] bufs = new ByteBuffer[testSize]; + long total = 0L; + for(int i = 0; i < testSize; i++) { + String source = "buffer" + i; + if (generator.nextBoolean()) + bufs[i] = ByteBuffer.allocateDirect(source.length()); + else + bufs[i] = ByteBuffer.allocate(source.length()); + + bufs[i].put(source.getBytes("8859_1")); + bufs[i].flip(); + total += bufs[i].remaining(); + } + + ByteBuffer[] bufsPlus1 = new ByteBuffer[bufs.length + 1]; + System.arraycopy(bufs, 0, bufsPlus1, 0, bufs.length); + + // Get a connection to the server + InetAddress lh = InetAddress.getLocalHost(); + InetSocketAddress isa = new InetSocketAddress(lh, port); + SocketChannel sc = Sockets.openRdmaSocketChannel(); + sc.connect(isa); + sc.configureBlocking(generator.nextBoolean()); + + // Write the data out + long rem = total; + while (rem > 0L) { + long bytesWritten; + if (setLength) { + bytesWritten = sc.write(bufsPlus1, 0, bufs.length); + } else { + bytesWritten = sc.write(bufs); + } + if (bytesWritten == 0) { + if (sc.isBlocking()) { + throw new RuntimeException("write did not block"); + } else { + System.err.println("Non-blocking write() wrote zero bytes"); + } + Thread.sleep(50); + } else { + rem -= bytesWritten; + } + } + + // Clean up + sc.close(); + } + + static class Server + extends TestThread + { + final int testSize; + final ServerSocketChannel ssc; + + Server(int testSize) throws IOException { + super("Server " + testSize); + this.testSize = testSize; + this.ssc = Sockets.openRdmaServerSocketChannel().bind(new InetSocketAddress(0)); + } + + int port() { + return ssc.socket().getLocalPort(); + } + + void go() throws Exception { + bufferTest(); + } + + void bufferTest() throws Exception { + long total = 0L; + ByteBuffer[] bufs = new ByteBuffer[testSize]; + for(int i=0; i 0) { + long bytesRead; + if (setLength) { + bytesRead = sc.read(bufsPlus1, 0, bufs.length); + } else { + bytesRead = sc.read(bufs); + } + if (bytesRead < 0) + break; + if (bytesRead == 0) { + if (sc.isBlocking()) { + throw new RuntimeException("read did not block"); + } else { + System.err.println + ("Non-blocking read() read zero bytes"); + } + Thread.sleep(50); + } + avail -= bytesRead; + } + + // Check results + for(int i=0; i after) { + System.err.println("Test failed: SO_RCVBUF"); + error = true; + } + + SocketChannel channel1 = Sockets.openRdmaSocketChannel(); + before = channel1.getOption(StandardSocketOptions.SO_SNDBUF); + channel1.setOption(StandardSocketOptions.SO_SNDBUF, Integer.MAX_VALUE); + after = channel1.getOption(StandardSocketOptions.SO_SNDBUF); + if (before > after) { + System.err.println("Test failed: SO_SNDBUF"); + error = true; + } + if (error) + throw new RuntimeException("Test failed"); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketOption/OptionsTest.java 2018-06-22 12:24:26.229671138 -0700 @@ -0,0 +1,180 @@ +/* + * 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 + * 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 8195160 + * @summary Unit test for SocketOption + * @requires (os.family == "linux") + * @requires !vm.graal.enabled + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true OptionsTest + */ +import java.lang.reflect.Method; +import java.net.*; +import java.util.*; +import jdk.net.Sockets; +import jdk.net.RdmaSocketOptions; + +public class OptionsTest { + + static class Test { + Test(SocketOption option, Object testValue) { + this.option = option; + this.testValue = testValue; + } + static Test create (SocketOption option, Object testValue) { + return new Test(option, testValue); + } + Object option; + Object testValue; + } + + // The tests set the option using the new API, read back the set value + // which could be diferent, and then use the legacy get API to check + // these values are the same + + static Test[] socketTests = new Test[] { + Test.create(StandardSocketOptions.SO_SNDBUF, Integer.valueOf(10 * 100)), + Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), + Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(10)), + Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), + }; + + static Test[] serverSocketTests = new Test[] { + Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)), + Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE), + }; + + static Test[] rdmaSocketOptionTests = new Test[] { + Test.create(RdmaSocketOptions.RDMA_SQSIZE, Integer.valueOf(8 * 100)), + Test.create(RdmaSocketOptions.RDMA_RQSIZE, Integer.valueOf(10 * 100)), + Test.create(RdmaSocketOptions.RDMA_INLINE, Integer.valueOf(20 * 100)), + }; + + static void doSocketTests() throws Exception { + try { + Socket c = Sockets.openRdmaSocket(); + + for (int i = 0; i < socketTests.length; i++) { + Test test = socketTests[i]; + c.setOption((SocketOption)test.option, test.testValue); + Object getval = c.getOption((SocketOption)test.option); + Object legacyget = legacyGetOption(Socket.class, c, test.option); + if (!getval.equals(legacyget)) { + Formatter f = new Formatter(); + f.format("S Err %d: %s/%s", i, getval, legacyget); + throw new RuntimeException(f.toString()); + } + } + + for (int i = 0; i < rdmaSocketOptionTests.length; i++) { + Test test = rdmaSocketOptionTests[i]; + c.setOption((SocketOption)test.option, test.testValue); + Object getval = c.getOption((SocketOption)test.option); + if (((Integer)getval).intValue() != + ((Integer)test.testValue).intValue()) + throw new RuntimeException("Test Failed"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + + static void doServerSocketTests() throws Exception { + try { + ServerSocket srv = Sockets.openRdmaServerSocket(); + for (int i = 0; i < serverSocketTests.length; i++) { + Test test = serverSocketTests[i]; + srv.setOption((SocketOption)test.option, test.testValue); + Object getval = srv.getOption((SocketOption)test.option); + Object legacyget = legacyGetOption( + ServerSocket.class, srv, test.option + ); + if (!getval.equals(legacyget)) { + Formatter f = new Formatter(); + f.format("SS Err %d: %s/%s", i, getval, legacyget); + throw new RuntimeException(f.toString()); + } + } + + for (int i = 0; i < rdmaSocketOptionTests.length; i++) { + Test test = rdmaSocketOptionTests[i]; + srv.setOption((SocketOption)test.option, test.testValue); + Object getval = srv.getOption((SocketOption)test.option); + if (((Integer)getval).intValue() != + ((Integer)test.testValue).intValue()) + throw new RuntimeException("Test Failed"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Test Failed"); + } + } + + static Object legacyGetOption( + Class type, Object s, Object option) + + throws Exception + { + if (type.equals(Socket.class)) { + Socket socket = (Socket)s; + Set> options = socket.supportedOptions(); + + if (option.equals(StandardSocketOptions.SO_SNDBUF)) { + return Integer.valueOf(socket.getSendBufferSize()); + } else if (option.equals(StandardSocketOptions.SO_RCVBUF)) { + return Integer.valueOf(socket.getReceiveBufferSize()); + } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { + return Boolean.valueOf(socket.getReuseAddress()); + } else if (option.equals(StandardSocketOptions.TCP_NODELAY)) { + return Boolean.valueOf(socket.getTcpNoDelay()); + } else if (option.equals(StandardSocketOptions.SO_LINGER)) { + return Integer.valueOf(socket.getSoLinger()); + } else { + throw new RuntimeException("unexecpted socket option"); + } + } else if (type.equals(ServerSocket.class)) { + ServerSocket socket = (ServerSocket)s; + Set> options = socket.supportedOptions(); + + if (option.equals(StandardSocketOptions.SO_RCVBUF)) { + return Integer.valueOf(socket.getReceiveBufferSize()); + } else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) { + return Boolean.valueOf(socket.getReuseAddress()); + } else { + throw new RuntimeException("unexecpted socket option"); + } + } + throw new RuntimeException("unexecpted socket type"); + } + + public static void main(String args[]) throws Exception { + if (!RsocketTest.isRsocketAvailable()) + return; + + doSocketTests(); + doServerSocketTests(); + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/SocketOption/UnsupportedOptionsTest.java 2018-06-22 12:24:26.733671124 -0700 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.*; +import java.util.ArrayList; +import java.util.List; +import jdk.net.Sockets; +import jdk.net.RdmaSocketOptions; + +/* @test + * @bug 8195160 + * @summary Test checks that UnsupportedOperationException for unsupported + * SOCKET_OPTIONS is thrown by both getOption() and setOption() methods. + * @requires (os.family == "linux") + * @requires !vm.graal.enabled + * @library .. /test/lib + * @build RsocketTest + * @run main/othervm -Djava.net.preferIPv4Stack=true UnsupportedOptionsTest + */ +public class UnsupportedOptionsTest { + + private static final List> socketOptions = new ArrayList<>(); + + static { + socketOptions.add(StandardSocketOptions.SO_RCVBUF); + socketOptions.add(StandardSocketOptions.SO_REUSEADDR); + socketOptions.add(StandardSocketOptions.SO_SNDBUF); + socketOptions.add(StandardSocketOptions.TCP_NODELAY); + socketOptions.add(StandardSocketOptions.SO_LINGER); + socketOptions.add(RdmaSocketOptions.RDMA_SQSIZE); + socketOptions.add(RdmaSocketOptions.RDMA_RQSIZE); + socketOptions.add(RdmaSocketOptions.RDMA_INLINE); + } + + public static void main(String[] args) throws IOException { + if (!RsocketTest.isRsocketAvailable()) + return; + + Socket s = Sockets.openRdmaSocket(); + ServerSocket ss = Sockets.openRdmaServerSocket(); + + for (SocketOption option : socketOptions) { + if (!s.supportedOptions().contains(option)) { + testUnsupportedSocketOption(s, option); + } + + if (!ss.supportedOptions().contains(option)) { + testUnsupportedSocketOption(ss, option); + } + } + } + + /* + * Check that UnsupportedOperationException for unsupported option is + * thrown from both getOption() and setOption() methods. + */ + private static void testUnsupportedSocketOption(Object socket, + SocketOption option) { + testSet(socket, option); + testGet(socket, option); + } + + private static void testSet(Object socket, SocketOption option) { + try { + setOption(socket, option); + } catch (UnsupportedOperationException e) { + System.out.println("UnsupportedOperationException was throw " + + "as expected. Socket: " + socket + " Option: " + option); + return; + } catch (Exception e) { + throw new RuntimeException("FAIL. Unexpected exception.", e); + } + throw new RuntimeException("FAIL. UnsupportedOperationException " + + "hasn't been thrown. Socket: " + socket + " Option: " + option); + } + + private static void testGet(Object socket, SocketOption option) { + try { + getOption(socket, option); + } catch (UnsupportedOperationException e) { + System.out.println("UnsupportedOperationException was throw " + + "as expected. Socket: " + socket + " Option: " + option); + return; + } catch (Exception e) { + throw new RuntimeException("FAIL. Unexpected exception.", e); + } + throw new RuntimeException("FAIL. UnsupportedOperationException " + + "hasn't been thrown. Socket: " + socket + " Option: " + option); + } + + private static void getOption(Object socket, + SocketOption option) throws IOException { + if (socket instanceof Socket) { + ((Socket) socket).getOption(option); + } else if (socket instanceof ServerSocket) { + ((ServerSocket) socket).getOption(option); + } else { + throw new RuntimeException("Unsupported socket type"); + } + } + + private static void setOption(Object socket, + SocketOption option) throws IOException { + if (socket instanceof Socket) { + ((Socket) socket).setOption(option, null); + } else if (socket instanceof ServerSocket) { + ((ServerSocket) socket).setOption(option, null); + } else { + throw new RuntimeException("Unsupported socket type"); + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/TestServers.java 2018-06-22 12:24:27.302671109 -0700 @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.*; +import java.net.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import jdk.net.Sockets; + +import jdk.test.lib.Utils; + + +public class TestServers { + + private TestServers() { } + + /** + * An abstract server identifies a server which listens on a port on on a + * given machine. + */ + static abstract class AbstractServer { + + private AbstractServer() { + } + + public abstract int getPort(); + + public abstract InetAddress getAddress(); + } + + /** + * A downgraded type of AbstractServer which will refuse connections. Note: + * use it once and throw it away - this implementation opens an anonymous + * socket and closes it, returning the address of the closed socket. If + * other servers are started afterwards, the address/port might get reused + * and become connectable again - so it's not a good idea to assume that + * connections using this address/port will always be refused. Connections + * will be refused as long as the address/port of the refusing server has + * not been reused. + */ + static class RefusingServer extends AbstractServer { + + final InetAddress address; + final int port; + + private RefusingServer(InetAddress address, int port) { + this.address = address; + this.port = port; + } + + @Override + public int getPort() { + return port; + } + + @Override + public InetAddress getAddress() { + return address; + } + + public static RefusingServer newRefusingServer() throws IOException { + return new RefusingServer(InetAddress.getLocalHost(), + Utils.refusingEndpoint().getPort()); + } + } + + /** + * An abstract class for implementing small TCP servers for the nio tests + * purposes. Disclaimer: This is a naive implementation that uses the old + * networking APIs (not those from {@code java.nio.*}) and shamelessly + * extends/creates Threads instead of using an executor service. + */ + static abstract class AbstractTcpServer extends AbstractServer + implements Runnable, Closeable { + + protected final long linger; // #of ms to wait before responding + private Thread acceptThread; // thread waiting for accept + // list of opened connections that should be closed on close. + private List connections = new ArrayList<>(); + private ServerSocket serverSocket; // the server socket + private boolean started = false; // whether the server is started + Throwable error = null; + + /** + * Creates a new abstract TCP server. + * + * @param linger the amount of time the server should wait before + * responding to requests. + */ + protected AbstractTcpServer(long linger) { + this.linger = linger; + } + + /** + * The local port to which the server is bound. + * + * @return The local port to which the server is bound. + * @exception IllegalStateException is thrown if the server is not + * started. + */ + @Override + public final synchronized int getPort() { + if (!started) { + throw new IllegalStateException("Not started"); + } + return serverSocket.getLocalPort(); + } + + /** + * The local address to which the server is bound. + * + * @return The local address to which the server is bound. + * @exception IllegalStateException is thrown if the server is not + * started. + */ + @Override + public final synchronized InetAddress getAddress() { + if (!started) { + throw new IllegalStateException("Not started"); + } + return serverSocket.getInetAddress(); + } + + /** + * Tells whether the server is started. + * + * @return true if the server is started. + */ + public final synchronized boolean isStarted() { + return started; + } + + /** + * Creates a new server socket. + * + * @param port local port to bind to. + * @param backlog requested maximum length of the queue of incoming + * connections. + * @param address local address to bind to. + * @return a new bound server socket ready to accept connections. + * @throws IOException if the socket cannot be created or bound. + */ + protected ServerSocket newServerSocket(int port, int backlog, + InetAddress address) + throws IOException { + ServerSocket ss = Sockets.openRdmaServerSocket(); + ss.bind(new InetSocketAddress(address, port), backlog); + return ss; + } + + /** + * Starts listening for connections. + * + * @throws IOException if the server socket cannot be created or bound. + */ + public final synchronized void start() throws IOException { + if (started) { + return; + } + final ServerSocket socket = + newServerSocket(0, 100, InetAddress.getLocalHost()); + serverSocket = socket; + acceptThread = new Thread(this); + acceptThread.setDaemon(true); + acceptThread.start(); + started = true; + } + + /** + * Calls {@code Thread.sleep(linger);} + */ + protected final void lingerIfRequired() { + if (linger > 0) { + try { + Thread.sleep(linger); + } catch (InterruptedException x) { + Thread.interrupted(); + final ServerSocket socket = serverSocket(); + if (socket != null && !socket.isClosed()) { + System.err.println("Thread interrupted..."); + } + } + } + } + + final synchronized ServerSocket serverSocket() { + return this.serverSocket; + } + + /** + * The main accept loop. + */ + @Override + public final void run() { + final ServerSocket sSocket = serverSocket(); + try { + Socket s; + while (isStarted() && !Thread.interrupted() + && (s = sSocket.accept()) != null) { + lingerIfRequired(); + listen(s); + } + } catch (Exception x) { + error = x; + } finally { + synchronized (this) { + if (!sSocket.isClosed()) { + try { + sSocket.close(); + } catch (IOException x) { + System.err.println("Failed to close server socket"); + } + } + if (started && this.serverSocket == sSocket) { + started = false; + this.serverSocket = null; + this.acceptThread = null; + } + } + } + } + + /** + * Represents a connection accepted by the server. + */ + protected abstract class TcpConnectionThread extends Thread { + + protected final Socket socket; + + protected TcpConnectionThread(Socket socket) { + this.socket = socket; + this.setDaemon(true); + } + + public void close() throws IOException { + socket.close(); + interrupt(); + } + } + + /** + * Creates a new TcpConnnectionThread to handle the connection through + * an accepted socket. + * + * @param s the socket returned by {@code serverSocket.accept()}. + * @return a new TcpConnnectionThread to handle the connection through + * an accepted socket. + */ + protected abstract TcpConnectionThread createConnection(Socket s); + + /** + * Creates and starts a new TcpConnectionThread to handle the accepted + * socket. + * + * @param s the socket returned by {@code serverSocket.accept()}. + */ + private synchronized void listen(Socket s) { + TcpConnectionThread c = createConnection(s); + c.start(); + addConnection(c); + } + + /** + * Add the connection to the list of accepted connections. + * + * @param connection an accepted connection. + */ + protected synchronized void addConnection( + TcpConnectionThread connection) { + connections.add(connection); + } + + /** + * Remove the connection from the list of accepted connections. + * + * @param connection an accepted connection. + */ + protected synchronized void removeConnection( + TcpConnectionThread connection) { + connections.remove(connection); + } + + /** + * Close the server socket and all the connections present in the list + * of accepted connections. + * + * @throws IOException + */ + @Override + public synchronized void close() throws IOException { + if (serverSocket != null && !serverSocket.isClosed()) { + serverSocket.close(); + } + if (acceptThread != null) { + acceptThread.interrupt(); + } + int failed = 0; + for (TcpConnectionThread c : connections) { + try { + c.close(); + } catch (IOException x) { + // no matter - we're closing. + failed++; + } + } + connections.clear(); + if (failed > 0) { + throw new IOException("Failed to close some connections"); + } + } + } + + /** + * A small TCP Server that emulates the echo service for tests purposes. See + * http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous + * port - NOT the standard port 7. We don't guarantee that its behavior + * exactly matches the RFC - the only purpose of this server is to have + * something that responds to nio tests... + */ + static class EchoServer extends AbstractTcpServer { + + public EchoServer() { + this(0L); + } + + public EchoServer(long linger) { + super(linger); + } + + @Override + protected TcpConnectionThread createConnection(Socket s) { + return new EchoConnection(s); + } + + private final class EchoConnection extends TcpConnectionThread { + + public EchoConnection(Socket socket) { + super(socket); + } + + @Override + public void run() { + try { + final InputStream is = socket.getInputStream(); + final OutputStream out = socket.getOutputStream(); + byte[] b = new byte[255]; + int n; + while ((n = is.read(b)) > 0) { + lingerIfRequired(); + out.write(b, 0, n); + } + } catch (IOException io) { + // fall through to finally + } finally { + if (!socket.isClosed()) { + try { + socket.close(); + } catch (IOException x) { + System.err.println( + "Failed to close echo connection socket"); + } + } + removeConnection(this); + } + } + } + + public static EchoServer startNewServer() throws IOException { + return startNewServer(0); + } + + public static EchoServer startNewServer(long linger) throws IOException { + final EchoServer echoServer = new EchoServer(linger); + echoServer.start(); + return echoServer; + } + } + + + /** + * A small TCP Server that accept connections but does not response to any input. + */ + static final class NoResponseServer extends EchoServer { + public NoResponseServer() { + super(Long.MAX_VALUE); + } + + public static NoResponseServer startNewServer() throws IOException { + final NoResponseServer noResponseServer = new NoResponseServer(); + noResponseServer.start(); + return noResponseServer; + } + } + + + /** + * A small TCP server that emulates the Day & Time service for tests + * purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server + * uses an anonymous port - NOT the standard port 13. We don't guarantee + * that its behavior exactly matches the RFC - the only purpose of this + * server is to have something that responds to nio tests... + */ + static final class DayTimeServer extends AbstractTcpServer { + + public DayTimeServer() { + this(0L); + } + + public DayTimeServer(long linger) { + super(linger); + } + + @Override + protected TcpConnectionThread createConnection(Socket s) { + return new DayTimeServerConnection(s); + } + + @Override + protected void addConnection(TcpConnectionThread connection) { + // do nothing - the connection just write the date and terminates. + } + + @Override + protected void removeConnection(TcpConnectionThread connection) { + // do nothing - we're not adding connections to the list... + } + + private final class DayTimeServerConnection extends TcpConnectionThread { + + public DayTimeServerConnection(Socket socket) { + super(socket); + } + + @Override + public void run() { + try { + final OutputStream out = socket.getOutputStream(); + lingerIfRequired(); + out.write(new Date(System.currentTimeMillis()) + .toString().getBytes("US-ASCII")); + out.flush(); + } catch (IOException io) { + // fall through to finally + } finally { + if (!socket.isClosed()) { + try { + socket.close(); + } catch (IOException x) { + System.err.println( + "Failed to close echo connection socket"); + } + } + } + } + } + + public static DayTimeServer startNewServer() + throws IOException { + return startNewServer(0); + } + + public static DayTimeServer startNewServer(long linger) + throws IOException { + final DayTimeServer daytimeServer = new DayTimeServer(linger); + daytimeServer.start(); + return daytimeServer; + } + } +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/TestThread.java 2018-06-22 12:24:27.814671095 -0700 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2000, 2002, 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. + */ + +/* Utility class for test threads + * + */ + +import java.io.*; + + +public abstract class TestThread + extends Thread +{ + Exception failure = null; + String name; + protected final PrintStream log; + Thread main; + + TestThread(String name, PrintStream log) { + super("TestThread-" + name); + this.name = name; + this.log = log; + this.main = Thread.currentThread(); + setDaemon(true); + } + + TestThread(String name) { + this(name, System.err); + } + + abstract void go() throws Exception; + + public void run() { + try { + go(); + } catch (Exception x) { + failure = x; + main.interrupt(); + } + } + + int finish(long timeout) { + try { + join(timeout); + } catch (InterruptedException x) { } + if (isAlive() && (failure == null)) + failure = new Exception(name + ": Timed out"); + if (failure != null) { + failure.printStackTrace(log); + return 0; + } + return 1; + } + + void finishAndThrow(long timeout) throws Exception { + try { + join(timeout); + } catch (InterruptedException x) { } + if (failure != null) + failure = new Exception(name + " threw an exception", + failure); + if (isAlive() && (failure == null)) + failure = new Exception(name + " timed out"); + if (failure != null) + throw failure; + } + + public String toString() { + return name; + } + +} --- /dev/null 2018-06-20 11:01:03.657616567 -0700 +++ new/test/jdk/jdk/net/Sockets/rsocket/libRsocketTest.c 2018-06-22 12:24:28.401671079 -0700 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. 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. + */ + +/* + * Test if rsocket is available + */ + +#include +#include "jni.h" + +/* + * Class: RsocketTest + * Method: isRsocketAvailable0 + * Signature: ()Z + */ +JNIEXPORT jboolean Java_RsocketTest_isRsocketAvailable0(JNIEnv *env, jclass cls) { + int s = rsocket(AF_INET, SOCK_STREAM, 0); + if (s > 0) { + return JNI_TRUE; + } + return JNI_FALSE; +}