8304919: Implementation of Virtual Threads

Reviewed-by: lmesnik, cjplummer, psandoz, mchung, sspitsyn, jpai
This commit is contained in:
Alan Bateman 2023-04-11 05:49:54 +00:00
parent 39398075b7
commit 2586f36120
205 changed files with 1379 additions and 1342 deletions

View file

@ -29,9 +29,11 @@ import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import jdk.internal.misc.CarrierThreadLocal;
import jdk.internal.misc.TerminatingThreadLocal;
import sun.security.action.GetPropertyAction;
/**
* This class provides thread-local variables. These variables differ from
@ -77,6 +79,8 @@ import jdk.internal.misc.TerminatingThreadLocal;
* @since 1.2
*/
public class ThreadLocal<T> {
private static final boolean TRACE_VTHREAD_LOCALS = traceVirtualThreadLocals();
/**
* ThreadLocals rely on per-thread linear-probe hash maps attached
* to each thread (Thread.threadLocals and
@ -161,11 +165,8 @@ public class ThreadLocal<T> {
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
* If the current thread does not support thread locals then
* this method returns its {@link #initialValue}.
*
* @return the current thread's value of this thread-local
* @see Thread.Builder#allowSetThreadLocals(boolean)
*/
public T get() {
return get(Thread.currentThread());
@ -183,15 +184,11 @@ public class ThreadLocal<T> {
private T get(Thread t) {
ThreadLocalMap map = getMap(t);
if (map != null) {
if (map == ThreadLocalMap.NOT_SUPPORTED) {
return initialValue();
} else {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T) e.value;
return result;
}
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T) e.value;
return result;
}
}
return setInitialValue(t);
@ -211,7 +208,7 @@ public class ThreadLocal<T> {
private boolean isPresent(Thread t) {
ThreadLocalMap map = getMap(t);
if (map != null && map != ThreadLocalMap.NOT_SUPPORTED) {
if (map != null) {
return map.getEntry(this) != null;
} else {
return false;
@ -227,7 +224,6 @@ public class ThreadLocal<T> {
private T setInitialValue(Thread t) {
T value = initialValue();
ThreadLocalMap map = getMap(t);
assert map != ThreadLocalMap.NOT_SUPPORTED;
if (map != null) {
map.set(this, value);
} else {
@ -236,6 +232,9 @@ public class ThreadLocal<T> {
if (this instanceof TerminatingThreadLocal<?> ttl) {
TerminatingThreadLocal.register(ttl);
}
if (TRACE_VTHREAD_LOCALS) {
dumpStackIfVirtualThread();
}
return value;
}
@ -247,14 +246,12 @@ public class ThreadLocal<T> {
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*
* @throws UnsupportedOperationException if the current thread is not
* allowed to set its copy of thread-local variables
*
* @see Thread.Builder#allowSetThreadLocals(boolean)
*/
public void set(T value) {
set(Thread.currentThread(), value);
if (TRACE_VTHREAD_LOCALS) {
dumpStackIfVirtualThread();
}
}
void setCarrierThreadLocal(T value) {
@ -264,9 +261,6 @@ public class ThreadLocal<T> {
private void set(Thread t, T value) {
ThreadLocalMap map = getMap(t);
if (map == ThreadLocalMap.NOT_SUPPORTED) {
throw new UnsupportedOperationException();
}
if (map != null) {
map.set(this, value);
} else {
@ -296,7 +290,7 @@ public class ThreadLocal<T> {
private void remove(Thread t) {
ThreadLocalMap m = getMap(t);
if (m != null && m != ThreadLocalMap.NOT_SUPPORTED) {
if (m != null) {
m.remove(this);
}
}
@ -394,9 +388,6 @@ public class ThreadLocal<T> {
}
}
// Placeholder when thread locals not supported
static final ThreadLocalMap NOT_SUPPORTED = new ThreadLocalMap();
/**
* The initial capacity -- MUST be a power of two.
*/
@ -807,4 +798,43 @@ public class ThreadLocal<T> {
}
}
}
/**
* Reads the value of the jdk.traceVirtualThreadLocals property to determine if
* a stack trace should be printed when a virtual thread sets a thread local.
*/
private static boolean traceVirtualThreadLocals() {
String propValue = GetPropertyAction.privilegedGetProperty("jdk.traceVirtualThreadLocals");
return (propValue != null)
&& (propValue.isEmpty() || Boolean.parseBoolean(propValue));
}
/**
* Print a stack trace if the current thread is a virtual thread.
*/
static void dumpStackIfVirtualThread() {
if (Thread.currentThread() instanceof VirtualThread vthread) {
try {
var stack = StackWalkerHolder.STACK_WALKER.walk(s ->
s.skip(1) // skip caller
.collect(Collectors.toList()));
// switch to carrier thread to avoid recursive use of thread-locals
vthread.executeOnCarrierThread(() -> {
System.out.println(vthread);
for (StackWalker.StackFrame frame : stack) {
System.out.format(" %s%n", frame.toStackTraceElement());
}
return null;
});
} catch (Exception e) {
throw new InternalError(e);
}
}
}
private static class StackWalkerHolder {
static final StackWalker STACK_WALKER = StackWalker.getInstance();
}
}