# HG changeset patch # User alanb # Date 1575130879 0 # Node ID 289000934908ac337e5e3694dacddb50695f817e # Parent f280911d34270ee6cd1a8aea3e15fce527e4744d 8234805: (dc) Remove JNI upcall from DatagramChannel.receive implementation Reviewed-by: dfuchs, chegar diff -r f280911d3427 -r 289000934908 src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java --- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Fri Nov 29 14:11:50 2019 -0800 +++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Sat Nov 30 16:21:19 2019 +0000 @@ -90,12 +90,12 @@ // Our file descriptor private final FileDescriptor fd; private final int fdVal; - private final Cleanable cleaner; - // Cached InetAddress and port for unconnected DatagramChannels - // used by receive0 - private InetAddress cachedSenderInetAddress; - private int cachedSenderPort; + // Native buffer for socket address used by receive0, protected by readLock + private final NativeSocketAddress socketAddress; + + // Cleaner to close file descriptor and free native socket address + private final Cleanable cleaner; // Lock held by current reading or connecting thread private final ReentrantLock readLock = new ReentrantLock(); @@ -154,6 +154,9 @@ throws IOException { super(sp); + + this.socketAddress = new NativeSocketAddress(); + ResourceManager.beforeUdpCreate(); try { this.family = Net.isIPv6Available() @@ -163,9 +166,12 @@ this.fdVal = IOUtil.fdVal(fd); } catch (IOException ioe) { ResourceManager.afterUdpClose(); + socketAddress.free(); throw ioe; } - this.cleaner = CleanerFactory.cleaner().register(this, closerFor(fd)); + + Runnable releaser = releaserFor(fd, socketAddress); + this.cleaner = CleanerFactory.cleaner().register(this, releaser); } public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family) @@ -183,6 +189,8 @@ } } + this.socketAddress = new NativeSocketAddress(); + ResourceManager.beforeUdpCreate(); try { this.family = family; @@ -190,9 +198,12 @@ this.fdVal = IOUtil.fdVal(fd); } catch (IOException ioe) { ResourceManager.afterUdpClose(); + socketAddress.free(); throw ioe; } - this.cleaner = CleanerFactory.cleaner().register(this, closerFor(fd)); + + Runnable releaser = releaserFor(fd, socketAddress); + this.cleaner = CleanerFactory.cleaner().register(this, releaser); } public DatagramChannelImpl(SelectorProvider sp, FileDescriptor fd) @@ -200,6 +211,13 @@ { super(sp); + try { + this.socketAddress = new NativeSocketAddress(); + } catch (OutOfMemoryError e) { + nd.close(fd); + throw e; + } + // increment UDP count to match decrement when closing ResourceManager.beforeUdpCreate(); @@ -208,7 +226,10 @@ : StandardProtocolFamily.INET; this.fd = fd; this.fdVal = IOUtil.fdVal(fd); - this.cleaner = CleanerFactory.cleaner().register(this, closerFor(fd)); + + Runnable releaser = releaserFor(fd, socketAddress); + this.cleaner = CleanerFactory.cleaner().register(this, releaser); + synchronized (stateLock) { this.localAddress = Net.localAddress(fd); } @@ -450,8 +471,6 @@ } } - private SocketAddress sender; // Set by receive0 (## ugh) - @Override public SocketAddress receive(ByteBuffer dst) throws IOException { if (dst.isReadOnly()) @@ -459,33 +478,31 @@ readLock.lock(); try { boolean blocking = isBlocking(); - boolean completed = false; - int n = 0; + SocketAddress sender = null; try { SocketAddress remote = beginRead(blocking, false); boolean connected = (remote != null); SecurityManager sm = System.getSecurityManager(); - if (connected || (sm == null)) { // connected or no security manager - n = receive(dst, connected); + int n = receive(dst, connected); if (blocking) { while (IOStatus.okayToRetry(n) && isOpen()) { park(Net.POLLIN); n = receive(dst, connected); } } + if (n >= 0) { + // sender address is in socket address buffer + sender = socketAddress.toInetSocketAddress(); + } } else { // security manager and unconnected - n = untrustedReceive(dst); + sender = untrustedReceive(dst); } - if (n == IOStatus.UNAVAILABLE) - return null; - completed = (n > 0) || (n == 0 && isOpen()); return sender; } finally { - endRead(blocking, completed); - assert IOStatus.check(n); + endRead(blocking, (sender != null)); } } finally { readLock.unlock(); @@ -498,10 +515,8 @@ * into a buffer that is not accessible to the user. The datagram is copied * into the user's buffer when the sender address is accepted by the security * manager. - * - * @return the size of the datagram or IOStatus.UNAVAILABLE */ - private int untrustedReceive(ByteBuffer dst) throws IOException { + private SocketAddress untrustedReceive(ByteBuffer dst) throws IOException { SecurityManager sm = System.getSecurityManager(); assert readLock.isHeldByCurrentThread() && sm != null && remoteAddress == null; @@ -516,18 +531,21 @@ park(Net.POLLIN); n = receive(bb, false); } - } else if (n == IOStatus.UNAVAILABLE) { - return n; } - InetSocketAddress isa = (InetSocketAddress) sender; - try { - sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort()); - bb.flip(); - dst.put(bb); - return n; - } catch (SecurityException se) { - // ignore datagram - bb.clear(); + if (n >= 0) { + // sender address is in socket address buffer + InetSocketAddress isa = socketAddress.toInetSocketAddress(); + try { + sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort()); + bb.flip(); + dst.put(bb); + return isa; + } catch (SecurityException se) { + // ignore datagram + bb.clear(); + } + } else { + return null; } } } finally { @@ -584,21 +602,22 @@ throws IOException { assert readLock.isHeldByCurrentThread() && isBlocking(); - boolean completed = false; - int n = 0; + SocketAddress sender = null; try { SocketAddress remote = beginRead(true, false); boolean connected = (remote != null); - n = receive(dst, connected); - while (n == IOStatus.UNAVAILABLE && isOpen()) { + int n = receive(dst, connected); + while (IOStatus.okayToRetry(n) && isOpen()) { park(Net.POLLIN); n = receive(dst, connected); } - completed = (n > 0) || (n == 0 && isOpen()); + if (n >= 0) { + // sender address is in socket address buffer + sender = socketAddress.toInetSocketAddress(); + } return sender; } finally { - endRead(true, completed); - assert IOStatus.check(n); + endRead(true, (sender != null)); } } @@ -611,8 +630,7 @@ throws IOException { assert readLock.isHeldByCurrentThread() && isBlocking(); - boolean completed = false; - int n = 0; + SocketAddress sender = null; try { SocketAddress remote = beginRead(true, false); boolean connected = (remote != null); @@ -621,7 +639,7 @@ lockedConfigureBlocking(false); try { long startNanos = System.nanoTime(); - n = receive(dst, connected); + int n = receive(dst, connected); while (n == IOStatus.UNAVAILABLE && isOpen()) { long remainingNanos = nanos - (System.nanoTime() - startNanos); if (remainingNanos <= 0) { @@ -630,16 +648,17 @@ park(Net.POLLIN, remainingNanos); n = receive(dst, connected); } - completed = (n > 0) || (n == 0 && isOpen()); + if (n >= 0) { + // sender address is in socket address buffer + sender = socketAddress.toInetSocketAddress(); + } return sender; } finally { // restore socket to blocking mode (if channel is open) tryLockedConfigureBlocking(true); } - } finally { - endRead(true, completed); - assert IOStatus.check(n); + endRead(true, (sender != null)); } } @@ -671,7 +690,10 @@ boolean connected) throws IOException { - int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem, connected); + int n = receive0(fd, + ((DirectBuffer)bb).address() + pos, rem, + socketAddress.address(), + connected); if (n > 0) bb.position(pos + n); return n; @@ -1709,38 +1731,37 @@ } /** - * Returns an action to close the given file descriptor. + * Returns an action to release a the given file descriptor and free the + * given native socket address. */ - private static Runnable closerFor(FileDescriptor fd) { + private static Runnable releaserFor(FileDescriptor fd, NativeSocketAddress sa) { return () -> { try { nd.close(fd); } catch (IOException ioe) { throw new UncheckedIOException(ioe); } finally { - // decrement + // decrement socket count and release memory ResourceManager.afterUdpClose(); + sa.free(); } }; } // -- Native methods -- - private static native void initIDs(); - private static native void disconnect0(FileDescriptor fd, boolean isIPv6) throws IOException; - private native int receive0(FileDescriptor fd, long address, int len, - boolean connected) + private static native int receive0(FileDescriptor fd, long address, int len, + long senderAddress, boolean connected) throws IOException; - private native int send0(boolean preferIPv6, FileDescriptor fd, long address, - int len, InetAddress addr, int port) + private static native int send0(boolean preferIPv6, FileDescriptor fd, + long address, int len, InetAddress addr, int port) throws IOException; static { IOUtil.load(); - initIDs(); } } diff -r f280911d3427 -r 289000934908 src/java.base/share/classes/sun/nio/ch/NativeSocketAddress.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/nio/ch/NativeSocketAddress.java Sat Nov 30 16:21:19 2019 +0000 @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; + +import jdk.internal.misc.Unsafe; +import jdk.internal.util.ArraysSupport; + +/** + * A native socket address that is the union of struct sockaddr, struct sockaddr_in, + * and struct sockaddr_in6. + * + * This class is not thread safe. + */ +class NativeSocketAddress { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final long ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); + + private static final int AF_INET = AFINET(); + private static final int AF_INET6 = AFINET6(); + + private static final int SIZEOF_SOCKETADDRESS = sizeofSOCKETADDRESS(); + private static final int SIZEOF_FAMILY = sizeofFamily(); + private static final int OFFSET_FAMILY = offsetFamily(); + private static final int OFFSET_SIN4_PORT = offsetSin4Port(); + private static final int OFFSET_SIN4_ADDR = offsetSin4Addr(); + private static final int OFFSET_SIN6_PORT = offsetSin6Port(); + private static final int OFFSET_SIN6_ADDR = offsetSin6Addr(); + private static final int OFFSET_SIN6_SCOPE_ID = offsetSin6ScopeId(); + + // SOCKETADDRESS + private final long address; + + // cached copy of SOCKETADDRESS and the corresponding InetSocketAddress + private final long cachedSocketAddress; + private InetSocketAddress cachedInetSocketAddress; + + NativeSocketAddress() { + // allocate 2 * SOCKETADDRESS + int size = SIZEOF_SOCKETADDRESS << 1; + long base = UNSAFE.allocateMemory(size); + UNSAFE.setMemory(base, size, (byte) 0); + + this.address = base; + this.cachedSocketAddress = base + SIZEOF_SOCKETADDRESS; + } + + long address() { + return address; + } + + void free() { + UNSAFE.freeMemory(address); + } + + /** + * Return an InetSocketAddress to represent the socket address in this buffer. + * @throws SocketException if the socket address is not AF_INET or AF_INET6 + */ + InetSocketAddress toInetSocketAddress() throws SocketException { + // return cached InetSocketAddress if the SOCKETADDRESS bytes match + if (cachedInetSocketAddress != null && mismatch() < 0) { + return cachedInetSocketAddress; + } + + // decode SOCKETADDRESS to InetSocketAddress + int family = family(); + if (family != AF_INET && family != AF_INET6) + throw new SocketException("Socket family not recognized"); + var isa = new InetSocketAddress(address(family), port(family)); + + // copy SOCKETADDRESS and InetSocketAddress + UNSAFE.copyMemory(null, address, null, cachedSocketAddress, SIZEOF_SOCKETADDRESS); + this.cachedInetSocketAddress = isa; + return isa; + } + + /** + * Find a mismatch between the SOCKETADDRESS structures stored at address + * and cachedSocketAddress. + * @return the byte offset of the first mismatch or -1 if no mismatch + */ + private int mismatch() { + int i = ArraysSupport.vectorizedMismatch(null, + address, + null, + cachedSocketAddress, + SIZEOF_SOCKETADDRESS, + ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE); + if (i >= 0) + return i; + i = SIZEOF_SOCKETADDRESS - ~i; + for (; i < SIZEOF_SOCKETADDRESS; i++) { + if (UNSAFE.getByte(address + i) != UNSAFE.getByte(cachedSocketAddress + i)) { + return i; + } + } + return -1; + } + + @Override + public String toString() { + int family = family(); + if (family == AF_INET || family == AF_INET6) { + return ((family == AF_INET) ? "AF_INET" : "AF_INET6") + + ", address=" + address(family) + ", port=" + port(family); + } else { + return ""; + } + } + + /** + * Return the value of the sa_family field. + */ + private int family() { + if (SIZEOF_FAMILY == 1) { + return UNSAFE.getByte(address + OFFSET_FAMILY); + } else if (SIZEOF_FAMILY == 2) { + return UNSAFE.getShort(address + OFFSET_FAMILY); + } else { + throw new InternalError(); + } + } + + /** + * Return the value of the sin4_port or sin6_port field. These fields are + * stored in network order. + */ + private int port(int family) { + byte b1, b2; + if (family == AF_INET) { + b1 = UNSAFE.getByte(address + OFFSET_SIN4_PORT); + b2 = UNSAFE.getByte(address + OFFSET_SIN4_PORT + 1); + } else { + b1 = UNSAFE.getByte(address + OFFSET_SIN6_PORT); + b2 = UNSAFE.getByte(address + OFFSET_SIN6_PORT + 1); + } + return (Byte.toUnsignedInt(b1) << 8) + Byte.toUnsignedInt(b2); + } + + /** + * Return an InetAddress to represent the value of the address in the + * sin4_addr or sin6_addr fields. + */ + private InetAddress address(int family) { + int len; + int offset; + int scope_id; + if (family == AF_INET) { + len = 4; + offset = OFFSET_SIN4_ADDR; + scope_id = 0; + } else { + len = 16; + offset = OFFSET_SIN6_ADDR; + scope_id = UNSAFE.getInt(address + OFFSET_SIN6_SCOPE_ID); + } + byte[] bytes = new byte[len]; + UNSAFE.copyMemory(null, address + offset, bytes, ARRAY_BASE_OFFSET, len); + try { + if (scope_id == 0) { + return InetAddress.getByAddress(bytes); + } else { + return Inet6Address.getByAddress(null, bytes, scope_id); + } + } catch (UnknownHostException e) { + throw new InternalError(e); + } + } + + private static native int AFINET(); + private static native int AFINET6(); + private static native int sizeofSOCKETADDRESS(); + private static native int sizeofFamily(); + private static native int offsetFamily(); + private static native int offsetSin4Port(); + private static native int offsetSin4Addr(); + private static native int offsetSin6Port(); + private static native int offsetSin6Addr(); + private static native int offsetSin6ScopeId(); + + static { + IOUtil.load(); + } +} diff -r f280911d3427 -r 289000934908 src/java.base/share/native/libnio/ch/NativeSocketAddress.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/native/libnio/ch/NativeSocketAddress.c Sat Nov 30 16:21:19 2019 +0000 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + #include "jni.h" + #include "net_util.h" + + #include "sun_nio_ch_NativeSocketAddress.h" + + JNIEXPORT jint JNICALL + Java_sun_nio_ch_NativeSocketAddress_AFINET(JNIEnv* env, jclass clazz) + { + return AF_INET; + } + + JNIEXPORT jint JNICALL + Java_sun_nio_ch_NativeSocketAddress_AFINET6(JNIEnv* env, jclass clazz) + { + return AF_INET6; + } + + JNIEXPORT jint JNICALL + Java_sun_nio_ch_NativeSocketAddress_sizeofSOCKETADDRESS(JNIEnv* env, jclass clazz) + { + return sizeof(SOCKETADDRESS); + } + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_NativeSocketAddress_sizeofFamily(JNIEnv* env, jclass clazz) +{ + // sizeof(struct sockaddr, sa_family) + return sizeof(((struct sockaddr *)0)->sa_family); +} + + JNIEXPORT jint JNICALL + Java_sun_nio_ch_NativeSocketAddress_offsetFamily(JNIEnv* env, jclass clazz) + { + return offsetof(struct sockaddr, sa_family); + } + + JNIEXPORT jint JNICALL + Java_sun_nio_ch_NativeSocketAddress_offsetSin4Port(JNIEnv* env, jclass clazz) + { + return offsetof(struct sockaddr_in, sin_port); + } + + JNIEXPORT jint JNICALL + Java_sun_nio_ch_NativeSocketAddress_offsetSin4Addr(JNIEnv* env, jclass clazz) + { + return offsetof(struct sockaddr_in, sin_addr); + } + + JNIEXPORT jint JNICALL + Java_sun_nio_ch_NativeSocketAddress_offsetSin6Port(JNIEnv* env, jclass clazz) + { + return offsetof(struct sockaddr_in6, sin6_port); + } + + JNIEXPORT jint JNICALL + Java_sun_nio_ch_NativeSocketAddress_offsetSin6Addr(JNIEnv* env, jclass clazz) + { + return offsetof(struct sockaddr_in6, sin6_addr); + } + + JNIEXPORT jint JNICALL + Java_sun_nio_ch_NativeSocketAddress_offsetSin6ScopeId(JNIEnv* env, jclass clazz) + { + return offsetof(struct sockaddr_in6, sin6_scope_id); + } diff -r f280911d3427 -r 289000934908 src/java.base/unix/native/libnio/ch/DatagramChannelImpl.c --- a/src/java.base/unix/native/libnio/ch/DatagramChannelImpl.c Fri Nov 29 14:11:50 2019 -0800 +++ b/src/java.base/unix/native/libnio/ch/DatagramChannelImpl.c Sat Nov 30 16:21:19 2019 +0000 @@ -23,11 +23,6 @@ * questions. */ -#include "jni.h" -#include "jni_util.h" -#include "jvm.h" -#include "jlong.h" - #include #include #include @@ -39,49 +34,17 @@ #include #endif +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" #include "net_util.h" -#include "net_util_md.h" #include "nio.h" #include "nio_util.h" #include "sun_nio_ch_DatagramChannelImpl.h" -static jfieldID dci_senderID; /* sender in sun.nio.ch.DatagramChannelImpl */ -static jfieldID dci_senderAddrID; /* sender InetAddress in sun.nio.ch.DatagramChannelImpl */ -static jfieldID dci_senderPortID; /* sender port in sun.nio.ch.DatagramChannelImpl */ -static jclass isa_class; /* java.net.InetSocketAddress */ -static jmethodID isa_ctorID; /* .InetSocketAddress(InetAddress, int) */ - JNIEXPORT void JNICALL -Java_sun_nio_ch_DatagramChannelImpl_initIDs(JNIEnv *env, jclass clazz) -{ - clazz = (*env)->FindClass(env, "java/net/InetSocketAddress"); - CHECK_NULL(clazz); - isa_class = (*env)->NewGlobalRef(env, clazz); - if (isa_class == NULL) { - JNU_ThrowOutOfMemoryError(env, NULL); - return; - } - isa_ctorID = (*env)->GetMethodID(env, clazz, "", - "(Ljava/net/InetAddress;I)V"); - CHECK_NULL(isa_ctorID); - - clazz = (*env)->FindClass(env, "sun/nio/ch/DatagramChannelImpl"); - CHECK_NULL(clazz); - dci_senderID = (*env)->GetFieldID(env, clazz, "sender", - "Ljava/net/SocketAddress;"); - CHECK_NULL(dci_senderID); - dci_senderAddrID = (*env)->GetFieldID(env, clazz, - "cachedSenderInetAddress", - "Ljava/net/InetAddress;"); - CHECK_NULL(dci_senderAddrID); - dci_senderPortID = (*env)->GetFieldID(env, clazz, - "cachedSenderPort", "I"); - CHECK_NULL(dci_senderPortID); -} - -JNIEXPORT void JNICALL -Java_sun_nio_ch_DatagramChannelImpl_disconnect0(JNIEnv *env, jobject this, +Java_sun_nio_ch_DatagramChannelImpl_disconnect0(JNIEnv *env, jclass clazz, jobject fdo, jboolean isIPv6) { jint fd = fdval(env, fdo); @@ -122,17 +85,17 @@ } JNIEXPORT jint JNICALL -Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this, - jobject fdo, jlong address, - jint len, jboolean connected) +Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jclass clazz, + jobject fdo, jlong bufAddress, + jint len, jlong senderAddress, + jboolean connected) { jint fd = fdval(env, fdo); - void *buf = (void *)jlong_to_ptr(address); - SOCKETADDRESS sa; + void *buf = (void *)jlong_to_ptr(bufAddress); + SOCKETADDRESS *sa = (SOCKETADDRESS *)jlong_to_ptr(senderAddress); socklen_t sa_len = sizeof(SOCKETADDRESS); jboolean retry = JNI_FALSE; - jint n = 0; - jobject senderAddr; + jint n; if (len > MAX_PACKET_LEN) { len = MAX_PACKET_LEN; @@ -140,7 +103,7 @@ do { retry = JNI_FALSE; - n = recvfrom(fd, buf, len, 0, &sa.sa, &sa_len); + n = recvfrom(fd, buf, len, 0, (struct sockaddr *)sa, &sa_len); if (n < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return IOS_UNAVAILABLE; @@ -152,8 +115,7 @@ if (connected == JNI_FALSE) { retry = JNI_TRUE; } else { - JNU_ThrowByName(env, JNU_JAVANETPKG - "PortUnreachableException", 0); + JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0); return IOS_THROWN; } } else { @@ -162,41 +124,11 @@ } } while (retry == JNI_TRUE); - /* - * If the source address and port match the cached address - * and port in DatagramChannelImpl then we don't need to - * create InetAddress and InetSocketAddress objects. - */ - senderAddr = (*env)->GetObjectField(env, this, dci_senderAddrID); - if (senderAddr != NULL) { - if (!NET_SockaddrEqualsInetAddress(env, &sa, senderAddr)) { - senderAddr = NULL; - } else { - jint port = (*env)->GetIntField(env, this, dci_senderPortID); - if (port != NET_GetPortFromSockaddr(&sa)) { - senderAddr = NULL; - } - } - } - if (senderAddr == NULL) { - jobject isa = NULL; - int port = 0; - jobject ia = NET_SockaddrToInetAddress(env, &sa, &port); - if (ia != NULL) { - isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); - } - CHECK_NULL_RETURN(isa, IOS_THROWN); - - (*env)->SetObjectField(env, this, dci_senderAddrID, ia); - (*env)->SetIntField(env, this, dci_senderPortID, - NET_GetPortFromSockaddr(&sa)); - (*env)->SetObjectField(env, this, dci_senderID, isa); - } return n; } JNIEXPORT jint JNICALL -Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this, +Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jclass clazz, jboolean preferIPv6, jobject fdo, jlong address, jint len, jobject destAddress, jint destPort) { diff -r f280911d3427 -r 289000934908 src/java.base/windows/native/libnio/ch/DatagramChannelImpl.c --- a/src/java.base/windows/native/libnio/ch/DatagramChannelImpl.c Fri Nov 29 14:11:50 2019 -0800 +++ b/src/java.base/windows/native/libnio/ch/DatagramChannelImpl.c Sat Nov 30 16:21:19 2019 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,51 +23,16 @@ * questions. */ -#include "jni.h" -#include "jni_util.h" -#include "jvm.h" -#include "jlong.h" -#include -#include "sun_nio_ch_DatagramChannelImpl.h" -#include "nio.h" -#include "nio_util.h" -#include "net_util.h" #include -static jfieldID dci_senderID; /* sender in sun.nio.ch.DatagramChannelImpl */ -static jfieldID dci_senderAddrID; /* sender InetAddress in sun.nio.ch.DatagramChannelImpl */ -static jfieldID dci_senderPortID; /* sender port in sun.nio.ch.DatagramChannelImpl */ -static jclass isa_class; /* java.net.InetSocketAddress */ -static jmethodID isa_ctorID; /* java.net.InetSocketAddress(InetAddress, int) */ - +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" +#include "net_util.h" +#include "nio.h" +#include "nio_util.h" -JNIEXPORT void JNICALL -Java_sun_nio_ch_DatagramChannelImpl_initIDs(JNIEnv *env, jclass clazz) -{ - clazz = (*env)->FindClass(env, "java/net/InetSocketAddress"); - CHECK_NULL(clazz); - isa_class = (*env)->NewGlobalRef(env, clazz); - if (isa_class == NULL) { - JNU_ThrowOutOfMemoryError(env, NULL); - return; - } - isa_ctorID = (*env)->GetMethodID(env, clazz, "", - "(Ljava/net/InetAddress;I)V"); - CHECK_NULL(isa_ctorID); - - clazz = (*env)->FindClass(env, "sun/nio/ch/DatagramChannelImpl"); - CHECK_NULL(clazz); - dci_senderID = (*env)->GetFieldID(env, clazz, "sender", - "Ljava/net/SocketAddress;"); - CHECK_NULL(dci_senderID); - dci_senderAddrID = (*env)->GetFieldID(env, clazz, - "cachedSenderInetAddress", - "Ljava/net/InetAddress;"); - CHECK_NULL(dci_senderAddrID); - dci_senderPortID = (*env)->GetFieldID(env, clazz, - "cachedSenderPort", "I"); - CHECK_NULL(dci_senderPortID); -} +#include "sun_nio_ch_DatagramChannelImpl.h" /* * This function "purges" all outstanding ICMP port unreachable packets @@ -112,7 +77,7 @@ } JNIEXPORT void JNICALL -Java_sun_nio_ch_DatagramChannelImpl_disconnect0(JNIEnv *env, jobject this, +Java_sun_nio_ch_DatagramChannelImpl_disconnect0(JNIEnv *env, jclass clazz, jobject fdo, jboolean isIPv6) { jint fd = fdval(env, fdo); @@ -135,17 +100,17 @@ } JNIEXPORT jint JNICALL -Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this, - jobject fdo, jlong address, - jint len, jboolean connected) +Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jclass clazz, + jobject fdo, jlong bufAddress, + jint len, jlong senderAddress, + jboolean connected) { jint fd = fdval(env, fdo); - void *buf = (void *)jlong_to_ptr(address); - SOCKETADDRESS sa; - int sa_len = sizeof(sa); + void *buf = (void *)jlong_to_ptr(bufAddress); + SOCKETADDRESS *sa = (SOCKETADDRESS *)jlong_to_ptr(senderAddress); + int sa_len = sizeof(SOCKETADDRESS); BOOL retry = FALSE; jint n; - jobject senderAddr; do { retry = FALSE; @@ -153,7 +118,7 @@ (char *)buf, len, 0, - &sa.sa, + (struct sockaddr *)sa, &sa_len); if (n == SOCKET_ERROR) { @@ -162,7 +127,7 @@ /* Spec says the rest of the data will be discarded... */ n = len; } else if (theErr == WSAECONNRESET) { - purgeOutstandingICMP(env, this, fd); + purgeOutstandingICMP(env, clazz, fd); if (connected == JNI_FALSE) { retry = TRUE; } else { @@ -175,42 +140,11 @@ } } while (retry); - /* - * If the source address and port match the cached address - * and port in DatagramChannelImpl then we don't need to - * create InetAddress and InetSocketAddress objects. - */ - senderAddr = (*env)->GetObjectField(env, this, dci_senderAddrID); - if (senderAddr != NULL) { - if (!NET_SockaddrEqualsInetAddress(env, &sa, senderAddr)) { - senderAddr = NULL; - } else { - jint port = (*env)->GetIntField(env, this, dci_senderPortID); - if (port != NET_GetPortFromSockaddr(&sa)) { - senderAddr = NULL; - } - } - } - if (senderAddr == NULL) { - jobject isa = NULL; - int port; - jobject ia = NET_SockaddrToInetAddress(env, &sa, &port); - if (ia != NULL) { - isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); - } - CHECK_NULL_RETURN(isa, IOS_THROWN); - - // update cachedSenderInetAddress/cachedSenderPort - (*env)->SetObjectField(env, this, dci_senderAddrID, ia); - (*env)->SetIntField(env, this, dci_senderPortID, - NET_GetPortFromSockaddr(&sa)); - (*env)->SetObjectField(env, this, dci_senderID, isa); - } return n; } JNIEXPORT jint JNICALL -Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this, +Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jclass clazz, jboolean preferIPv6, jobject fdo, jlong address, jint len, jobject destAddress, jint destPort) diff -r f280911d3427 -r 289000934908 test/jdk/java/nio/channels/DatagramChannel/ManySenders.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/nio/channels/DatagramChannel/ManySenders.java Sat Nov 30 16:21:19 2019 +0000 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8234805 + * @summary Test that DatagramChannel.receive returns the expected sender address + * @run main ManySenders + * @run main/othervm -Djava.net.preferIPv4Stack=true ManySenders + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ManySenders { + public static void main(String[] args) throws Exception { + + // use addresses on interfaces that have the loopback and local host + InetAddress lh = InetAddress.getLocalHost(); + InetAddress lb = InetAddress.getLoopbackAddress(); + List addresses = Stream.concat( + NetworkInterface.getByInetAddress(lh).inetAddresses(), + NetworkInterface.getByInetAddress(lb).inetAddresses()) + .filter(ia -> !ia.isAnyLocalAddress()) + .distinct() + .collect(Collectors.toList()); + + // bind DatagramChannel to wildcard address so it can receive from any address + try (DatagramChannel reader = DatagramChannel.open()) { + reader.bind(new InetSocketAddress(0)); + for (InetAddress address : addresses) { + System.out.format("%n-- %s --%n", address.getHostAddress()); + + // send 3 datagrams from the given address to the reader + test(3, address, reader); + } + } + } + + static void test(int count, InetAddress address, DatagramChannel reader) throws Exception { + int remotePort = reader.socket().getLocalPort(); + InetSocketAddress remote = new InetSocketAddress(address, remotePort); + + try (DatagramChannel sender = DatagramChannel.open()) { + sender.bind(new InetSocketAddress(address, 0)); + + SocketAddress local = sender.getLocalAddress(); + byte[] bytes = serialize(local); + + SocketAddress previousSource = null; + for (int i = 0; i < count; i++) { + System.out.format("send %s -> %s%n", local, remote); + sender.send(ByteBuffer.wrap(bytes), remote); + + ByteBuffer bb = ByteBuffer.allocate(1000); + SocketAddress source = reader.receive(bb); + System.out.format("received datagram from %s%n", source); + + // check source address and payload + SocketAddress payload = deserialize(bb.array()); + if (!source.equals(local)) + throw new RuntimeException("source=" + source + ", expected=" + local); + if (!payload.equals(local)) + throw new RuntimeException("payload=" + payload + ", expected=" + local); + + // check that cached source was used + if (previousSource == null) { + previousSource = source; + } else if (source != previousSource) { + throw new RuntimeException("Cached SocketAddress not returned"); + } + } + } + } + + private static byte[] serialize(SocketAddress address) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(address); + oos.close(); + return baos.toByteArray(); + } + + private static SocketAddress deserialize(byte[] bytes) throws Exception { + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais); + return (SocketAddress) ois.readObject(); + } +} \ No newline at end of file