mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8234805: (dc) Remove JNI upcall from DatagramChannel.receive implementation
Reviewed-by: dfuchs, chegar
This commit is contained in:
parent
b08944dd96
commit
f3e90595aa
6 changed files with 535 additions and 226 deletions
|
@ -90,12 +90,12 @@ class DatagramChannelImpl
|
|||
// 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 @@ class DatagramChannelImpl
|
|||
throws IOException
|
||||
{
|
||||
super(sp);
|
||||
|
||||
this.socketAddress = new NativeSocketAddress();
|
||||
|
||||
ResourceManager.beforeUdpCreate();
|
||||
try {
|
||||
this.family = Net.isIPv6Available()
|
||||
|
@ -163,9 +166,12 @@ class DatagramChannelImpl
|
|||
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 @@ class DatagramChannelImpl
|
|||
}
|
||||
}
|
||||
|
||||
this.socketAddress = new NativeSocketAddress();
|
||||
|
||||
ResourceManager.beforeUdpCreate();
|
||||
try {
|
||||
this.family = family;
|
||||
|
@ -190,9 +198,12 @@ class DatagramChannelImpl
|
|||
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 @@ class DatagramChannelImpl
|
|||
{
|
||||
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 @@ class DatagramChannelImpl
|
|||
: 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 @@ class DatagramChannelImpl
|
|||
}
|
||||
}
|
||||
|
||||
private SocketAddress sender; // Set by receive0 (## ugh)
|
||||
|
||||
@Override
|
||||
public SocketAddress receive(ByteBuffer dst) throws IOException {
|
||||
if (dst.isReadOnly())
|
||||
|
@ -459,33 +478,31 @@ class DatagramChannelImpl
|
|||
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 @@ class DatagramChannelImpl
|
|||
* 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 @@ class DatagramChannelImpl
|
|||
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 @@ class DatagramChannelImpl
|
|||
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 @@ class DatagramChannelImpl
|
|||
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 @@ class DatagramChannelImpl
|
|||
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 @@ class DatagramChannelImpl
|
|||
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 @@ class DatagramChannelImpl
|
|||
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 @@ class DatagramChannelImpl
|
|||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
213
src/java.base/share/classes/sun/nio/ch/NativeSocketAddress.java
Normal file
213
src/java.base/share/classes/sun/nio/ch/NativeSocketAddress.java
Normal file
|
@ -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 "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue