8284161: Implementation of Virtual Threads (Preview)

Co-authored-by: Ron Pressler <rpressler@openjdk.org>
Co-authored-by: Alan Bateman <alanb@openjdk.org>
Co-authored-by: Erik Österlund <eosterlund@openjdk.org>
Co-authored-by: Andrew Haley <aph@openjdk.org>
Co-authored-by: Rickard Bäckman <rbackman@openjdk.org>
Co-authored-by: Markus Grönlund <mgronlun@openjdk.org>
Co-authored-by: Leonid Mesnik <lmesnik@openjdk.org>
Co-authored-by: Serguei Spitsyn <sspitsyn@openjdk.org>
Co-authored-by: Chris Plummer <cjplummer@openjdk.org>
Co-authored-by: Coleen Phillimore <coleenp@openjdk.org>
Co-authored-by: Robbin Ehn <rehn@openjdk.org>
Co-authored-by: Stefan Karlsson <stefank@openjdk.org>
Co-authored-by: Thomas Schatzl <tschatzl@openjdk.org>
Co-authored-by: Sergey Kuksenko <skuksenko@openjdk.org>
Reviewed-by: lancea, eosterlund, rehn, sspitsyn, stefank, tschatzl, dfuchs, lmesnik, dcubed, kevinw, amenkov, dlong, mchung, psandoz, bpb, coleenp, smarks, egahlin, mseledtsov, coffeys, darcy
This commit is contained in:
Alan Bateman 2022-05-07 08:06:16 +00:00
parent 5212535a27
commit 9583e3657e
1133 changed files with 95935 additions and 8335 deletions

View file

@ -29,11 +29,8 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.MulticastChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Objects;
import java.util.Set;
import sun.net.NetProperties;
import sun.nio.ch.DefaultSelectorProvider;
/**
@ -666,17 +663,33 @@ public class DatagramSocket implements java.io.Closeable {
}
/**
* Receives a datagram packet from this socket. When this method
* returns, the {@code DatagramPacket}'s buffer is filled with
* the data received. The datagram packet also contains the sender's
* Receives a datagram packet from this socket. This method blocks until a
* datagram is received.
*
* When this method returns, the {@code DatagramPacket}'s buffer is filled
* with the data received. The datagram packet also contains the sender's
* IP address, and the port number on the sender's machine.
* <p>
* This method blocks until a datagram is received. The
* {@code length} field of the datagram packet object contains
* The {@code length} field of the datagram packet object contains
* the length of the received message. If the message is longer than
* the packet's length, the message is truncated.
* <p>
* If there is a security manager, and the socket is not currently
*
* <p> This method is {@linkplain Thread#interrupt() interruptible} in the
* following circumstances:
* <ol>
* <li> The datagram socket is {@linkplain DatagramChannel#socket() associated}
* with a {@link DatagramChannel DatagramChannel}. In that case,
* interrupting a thread receiving a datagram packet will close the
* underlying channel and cause this method to throw {@link
* java.nio.channels.ClosedByInterruptException} with the interrupt
* status set.
* <li> The datagram socket uses the system-default socket implementation and
* a {@linkplain Thread#isVirtual() virtual thread} is receiving a
* datagram packet. In that case, interrupting the virtual thread will
* cause it to wakeup and close the socket. This method will then throw
* {@code SocketException} with the interrupt status set.
* </ol>
*
* <p> If there is a security manager, and the socket is not currently
* connected to a remote address, a packet cannot be received if the
* security manager's {@code checkAccept} method does not allow it.
* Datagrams that are not permitted by the security manager are silently

View file

@ -55,10 +55,10 @@ import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;
import jdk.internal.misc.VM;
import jdk.internal.access.JavaNetInetAddressAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Blocker;
import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.Stable;
import sun.net.ResolverProviderConfiguration;
import sun.security.action.*;
@ -972,6 +972,7 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
// in cache when the result is obtained
private static final class NameServiceAddresses implements Addresses {
private final String host;
private final ReentrantLock lookupLock = new ReentrantLock();
NameServiceAddresses(String host) {
this.host = host;
@ -982,7 +983,8 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
Addresses addresses;
// only one thread is doing lookup to name service
// for particular host at any time.
synchronized (this) {
lookupLock.lock();
try {
// re-check that we are still us + re-install us if slot empty
addresses = cache.putIfAbsent(host, this);
if (addresses == null) {
@ -1030,6 +1032,8 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
return inetAddresses;
}
// else addresses != this
} finally {
lookupLock.unlock();
}
// delegate to different addresses when we are already replaced
// but outside of synchronized block to avoid any chance of dead-locking
@ -1049,16 +1053,27 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
throws UnknownHostException {
Objects.requireNonNull(host);
Objects.requireNonNull(policy);
return Arrays.stream(impl.lookupAllHostAddr(host, policy));
InetAddress[] addrs;
long comp = Blocker.begin();
try {
addrs = impl.lookupAllHostAddr(host, policy);
} finally {
Blocker.end(comp);
}
return Arrays.stream(addrs);
}
public String lookupByAddress(byte[] addr)
throws UnknownHostException {
public String lookupByAddress(byte[] addr) throws UnknownHostException {
Objects.requireNonNull(addr);
if (addr.length != Inet4Address.INADDRSZ && addr.length != Inet6Address.INADDRSZ) {
throw new IllegalArgumentException("Invalid address length");
}
return impl.getHostByAddr(addr);
long comp = Blocker.begin();
try {
return impl.getHostByAddr(addr);
} finally {
Blocker.end(comp);
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2022, 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
@ -121,11 +121,10 @@ public class MulticastSocket extends DatagramSocket {
* Create a MulticastSocket that delegates to the given delegate if not null.
* @param delegate the delegate, can be null.
*/
MulticastSocket(MulticastSocket delegate) {
MulticastSocket(MulticastSocket delegate) {
super(delegate);
}
/**
* Constructs a multicast socket and binds it to any available port
* on the local host machine. The socket will be bound to the

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2022, 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
@ -27,6 +27,7 @@ package java.net;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.nio.channels.ServerSocketChannel;
import java.util.Objects;
import java.util.Set;
@ -481,7 +482,6 @@ public class ServerSocket implements java.io.Closeable {
* @see SecurityManager#checkConnect
* @since 1.4
*/
public SocketAddress getLocalSocketAddress() {
if (!isBound())
return null;
@ -492,7 +492,23 @@ public class ServerSocket implements java.io.Closeable {
* Listens for a connection to be made to this socket and accepts
* it. The method blocks until a connection is made.
*
* <p>A new Socket {@code s} is created and, if there
* <p> This method is {@linkplain Thread#interrupt() interruptible} in the
* following circumstances:
* <ol>
* <li> The socket is {@linkplain ServerSocketChannel#socket() associated}
* with a {@link ServerSocketChannel ServerSocketChannel}. In that
* case, interrupting a thread accepting a connection will close the
* underlying channel and cause this method to throw {@link
* java.nio.channels.ClosedByInterruptException} with the interrupt
* status set.
* <li> The socket uses the system-default socket implementation and a
* {@linkplain Thread#isVirtual() virtual thread} is accepting a
* connection. In that case, interrupting the virtual thread will
* cause it to wakeup and close the socket. This method will then throw
* {@code SocketException} with the interrupt status set.
* </ol>
*
* <p> A new Socket {@code s} is created and, if there
* is a security manager,
* the security manager's {@code checkAccept} method is called
* with {@code s.getInetAddress().getHostAddress()} and
@ -670,7 +686,18 @@ public class ServerSocket implements java.io.Closeable {
assert !(si instanceof DelegatingSocketImpl);
// accept a connection
impl.accept(si);
try {
impl.accept(si);
} catch (SocketTimeoutException e) {
throw e;
} catch (InterruptedIOException e) {
Thread thread = Thread.currentThread();
if (thread.isVirtual() && thread.isInterrupted()) {
close();
throw new SocketException("Closed by interrupt");
}
throw e;
}
// check permission, close SocketImpl/connection if denied
@SuppressWarnings("removal")

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2022, 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
@ -28,10 +28,12 @@ package java.net;
import sun.security.util.SecurityConstants;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.SocketChannel;
import java.util.Objects;
import java.util.Set;
@ -570,6 +572,21 @@ public class Socket implements java.io.Closeable {
/**
* Connects this socket to the server.
*
* <p> This method is {@linkplain Thread#interrupt() interruptible} in the
* following circumstances:
* <ol>
* <li> The socket is {@linkplain SocketChannel#socket() associated} with
* a {@link SocketChannel SocketChannel}.
* In that case, interrupting a thread establishing a connection will
* close the underlying channel and cause this method to throw
* {@link ClosedByInterruptException} with the interrupt status set.
* <li> The socket uses the system-default socket implementation and a
* {@linkplain Thread#isVirtual() virtual thread} is establishing a
* connection. In that case, interrupting the virtual thread will
* cause it to wakeup and close the socket. This method will then throw
* {@code SocketException} with the interrupt status set.
* </ol>
*
* @param endpoint the {@code SocketAddress}
* @throws IOException if an error occurs during the connection
* @throws java.nio.channels.IllegalBlockingModeException
@ -588,6 +605,21 @@ public class Socket implements java.io.Closeable {
* A timeout of zero is interpreted as an infinite timeout. The connection
* will then block until established or an error occurs.
*
* <p> This method is {@linkplain Thread#interrupt() interruptible} in the
* following circumstances:
* <ol>
* <li> The socket is {@linkplain SocketChannel#socket() associated} with
* a {@link SocketChannel SocketChannel}.
* In that case, interrupting a thread establishing a connection will
* close the underlying channel and cause this method to throw
* {@link ClosedByInterruptException} with the interrupt status set.
* <li> The socket uses the system-default socket implementation and a
* {@linkplain Thread#isVirtual() virtual thread} is establishing a
* connection. In that case, interrupting the virtual thread will
* cause it to wakeup and close the socket. This method will then throw
* {@code SocketException} with the interrupt status set.
* </ol>
*
* @param endpoint the {@code SocketAddress}
* @param timeout the timeout value to be used in milliseconds.
* @throws IOException if an error occurs during the connection
@ -630,7 +662,18 @@ public class Socket implements java.io.Closeable {
}
if (!created)
createImpl(true);
impl.connect(epoint, timeout);
try {
impl.connect(epoint, timeout);
} catch (SocketTimeoutException e) {
throw e;
} catch (InterruptedIOException e) {
Thread thread = Thread.currentThread();
if (thread.isVirtual() && thread.isInterrupted()) {
close();
throw new SocketException("Closed by interrupt");
}
throw e;
}
connected = true;
/*
* If the socket was not bound before the connect, it is now because
@ -886,6 +929,22 @@ public class Socket implements java.io.Closeable {
* is in non-blocking mode then the input stream's {@code read} operations
* will throw an {@link java.nio.channels.IllegalBlockingModeException}.
*
* <p> Reading from the input stream is {@linkplain Thread#interrupt()
* interruptible} in the following circumstances:
* <ol>
* <li> The socket is {@linkplain SocketChannel#socket() associated} with
* a {@link SocketChannel SocketChannel}.
* In that case, interrupting a thread reading from the input stream
* will close the underlying channel and cause the read method to
* throw {@link ClosedByInterruptException} with the interrupt
* status set.
* <li> The socket uses the system-default socket implementation and a
* {@linkplain Thread#isVirtual() virtual thread} is reading from the
* input stream. In that case, interrupting the virtual thread will
* cause it to wakeup and close the socket. The read method will then
* throw {@code SocketException} with the interrupt status set.
* </ol>
*
* <p>Under abnormal conditions the underlying connection may be
* broken by the remote host or the network software (for example
* a connection reset in the case of TCP connections). When a
@ -950,7 +1009,6 @@ public class Socket implements java.io.Closeable {
private static class SocketInputStream extends InputStream {
private final Socket parent;
private final InputStream in;
SocketInputStream(Socket parent, InputStream in) {
this.parent = parent;
this.in = in;
@ -963,13 +1021,23 @@ public class Socket implements java.io.Closeable {
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return in.read(b, off, len);
try {
return in.read(b, off, len);
} catch (SocketTimeoutException e) {
throw e;
} catch (InterruptedIOException e) {
Thread thread = Thread.currentThread();
if (thread.isVirtual() && thread.isInterrupted()) {
close();
throw new SocketException("Closed by interrupt");
}
throw e;
}
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void close() throws IOException {
parent.close();
@ -985,6 +1053,22 @@ public class Socket implements java.io.Closeable {
* operations will throw an {@link
* java.nio.channels.IllegalBlockingModeException}.
*
* <p> Writing to the output stream is {@linkplain Thread#interrupt()
* interruptible} in the following circumstances:
* <ol>
* <li> The socket is {@linkplain SocketChannel#socket() associated} with
* a {@link SocketChannel SocketChannel}.
* In that case, interrupting a thread writing to the output stream
* will close the underlying channel and cause the write method to
* throw {@link ClosedByInterruptException} with the interrupt status
* set.
* <li> The socket uses the system-default socket implementation and a
* {@linkplain Thread#isVirtual() virtual thread} is writing to the
* output stream. In that case, interrupting the virtual thread will
* cause it to wakeup and close the socket. The write method will then
* throw {@code SocketException} with the interrupt status set.
* </ol>
*
* <p> Closing the returned {@link java.io.OutputStream OutputStream}
* will close the associated socket.
*
@ -1032,9 +1116,17 @@ public class Socket implements java.io.Closeable {
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
try {
out.write(b, off, len);
} catch (InterruptedIOException e) {
Thread thread = Thread.currentThread();
if (thread.isVirtual() && thread.isInterrupted()) {
close();
throw new SocketException("Closed by interrupt");
}
throw e;
}
}
@Override
public void close() throws IOException {
parent.close();

View file

@ -45,6 +45,7 @@ import java.util.ServiceLoader;
import jdk.internal.access.JavaNetURLAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.ThreadTracker;
import jdk.internal.misc.VM;
import sun.net.util.IPAddressUtil;
import sun.security.util.SecurityConstants;
@ -1342,15 +1343,24 @@ public final class URL implements java.io.Serializable {
};
}
// Thread-local gate to prevent recursive provider lookups
private static ThreadLocal<Object> gate = new ThreadLocal<>();
private static class ThreadTrackHolder {
static final ThreadTracker TRACKER = new ThreadTracker();
}
private static Object tryBeginLookup() {
return ThreadTrackHolder.TRACKER.tryBegin();
}
private static void endLookup(Object key) {
ThreadTrackHolder.TRACKER.end(key);
}
@SuppressWarnings("removal")
private static URLStreamHandler lookupViaProviders(final String protocol) {
if (gate.get() != null)
Object key = tryBeginLookup();
if (key == null) {
throw new Error("Circular loading of URL stream handler providers detected");
gate.set(gate);
}
try {
return AccessController.doPrivileged(
new PrivilegedAction<>() {
@ -1366,7 +1376,7 @@ public final class URL implements java.io.Serializable {
}
});
} finally {
gate.set(null);
endLookup(key);
}
}