8286666: JEP 429: Implementation of Scoped Values (Incubator)

Reviewed-by: psandoz, dlong, alanb, mcimadamore
This commit is contained in:
Andrew Haley 2022-12-07 10:14:06 +00:00 committed by Alan Bateman
parent ccc69af966
commit 221e1a4260
61 changed files with 2889 additions and 230 deletions

View file

@ -86,6 +86,7 @@ import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;
import jdk.internal.vm.StackableScope;
import jdk.internal.vm.ThreadContainer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
import jdk.internal.vm.annotation.ChangesCurrentThread;
@ -2578,20 +2579,29 @@ public final class System {
return ((ThreadLocal<?>)local).isCarrierThreadLocalPresent();
}
public Object[] extentLocalCache() {
return Thread.extentLocalCache();
public Object[] scopedValueCache() {
return Thread.scopedValueCache();
}
public void setExtentLocalCache(Object[] cache) {
Thread.setExtentLocalCache(cache);
public void setScopedValueCache(Object[] cache) {
Thread.setScopedValueCache(cache);
}
public Object extentLocalBindings() {
return Thread.extentLocalBindings();
public Object scopedValueBindings() {
return Thread.scopedValueBindings();
}
public void setExtentLocalBindings(Object bindings) {
Thread.setExtentLocalBindings(bindings);
public Object findScopedValueBindings() {
return Thread.findScopedValueBindings();
}
public void setScopedValueBindings(Object bindings) {
Thread.setScopedValueBindings(bindings);
}
@ForceInline
public void ensureMaterializedForStackWalk(Object value) {
Thread.ensureMaterializedForStackWalk(value);
}
public Continuation getContinuation(Thread thread) {

View file

@ -25,6 +25,7 @@
package java.lang;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.AccessControlContext;
@ -49,9 +50,11 @@ import jdk.internal.misc.VM;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ExtentLocalContainer;
import jdk.internal.vm.ScopedValueContainer;
import jdk.internal.vm.StackableScope;
import jdk.internal.vm.ThreadContainer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import sun.nio.ch.Interruptible;
import sun.security.util.SecurityConstants;
@ -279,34 +282,44 @@ public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap inheritableThreadLocals;
/*
* Extent locals binding are maintained by the ExtentLocal class.
* Scoped value bindings are maintained by the ScopedValue class.
*/
private Object extentLocalBindings;
private Object scopedValueBindings;
static Object extentLocalBindings() {
return currentThread().extentLocalBindings;
// Special value to indicate this is a newly-created Thread
// Note that his must match the declaration in ScopedValue.
private static final Object NEW_THREAD_BINDINGS = Thread.class;
static Object scopedValueBindings() {
return currentThread().scopedValueBindings;
}
static void setExtentLocalBindings(Object bindings) {
currentThread().extentLocalBindings = bindings;
static void setScopedValueBindings(Object bindings) {
currentThread().scopedValueBindings = bindings;
}
/**
* Inherit the extent-local bindings from the given container.
* Search the stack for the most recent scoped-value bindings.
*/
@IntrinsicCandidate
static native Object findScopedValueBindings();
/**
* Inherit the scoped-value bindings from the given container.
* Invoked when starting a thread.
*/
void inheritExtentLocalBindings(ThreadContainer container) {
ExtentLocalContainer.BindingsSnapshot snapshot;
void inheritScopedValueBindings(ThreadContainer container) {
ScopedValueContainer.BindingsSnapshot snapshot;
if (container.owner() != null
&& (snapshot = container.extentLocalBindings()) != null) {
&& (snapshot = container.scopedValueBindings()) != null) {
// bindings established for running/calling an operation
Object bindings = snapshot.extentLocalBindings();
if (currentThread().extentLocalBindings != bindings) {
StructureViolationExceptions.throwException("Extent local bindings have changed");
Object bindings = snapshot.scopedValueBindings();
if (currentThread().scopedValueBindings != bindings) {
StructureViolationExceptions.throwException("Scoped value bindings have changed");
}
this.extentLocalBindings = bindings;
this.scopedValueBindings = bindings;
}
}
@ -393,13 +406,16 @@ public class Thread implements Runnable {
@IntrinsicCandidate
native void setCurrentThread(Thread thread);
// ExtentLocal support:
// ScopedValue support:
@IntrinsicCandidate
static native Object[] extentLocalCache();
static native Object[] scopedValueCache();
@IntrinsicCandidate
static native void setExtentLocalCache(Object[] cache);
static native void setScopedValueCache(Object[] cache);
@IntrinsicCandidate
static native void ensureMaterializedForStackWalk(Object o);
/**
* A hint to the scheduler that the current thread is willing to yield
@ -728,6 +744,10 @@ public class Thread implements Runnable {
this.contextClassLoader = ClassLoader.getSystemClassLoader();
}
}
// Special value to indicate this is a newly-created Thread
// Note that his must match the declaration in ScopedValue.
this.scopedValueBindings = NEW_THREAD_BINDINGS;
}
/**
@ -767,6 +787,9 @@ public class Thread implements Runnable {
this.contextClassLoader = ClassLoader.getSystemClassLoader();
}
// Special value to indicate this is a newly-created Thread
this.scopedValueBindings = NEW_THREAD_BINDINGS;
// create a FieldHolder object, needed when bound to an OS thread
if (bound) {
ThreadGroup g = Constants.VTHREAD_GROUP;
@ -1564,8 +1587,8 @@ public class Thread implements Runnable {
boolean started = false;
container.onStart(this); // may throw
try {
// extent locals may be inherited
inheritExtentLocalBindings(container);
// scoped values may be inherited
inheritScopedValueBindings(container);
start0();
started = true;
@ -1596,10 +1619,24 @@ public class Thread implements Runnable {
public void run() {
Runnable task = holder.task;
if (task != null) {
task.run();
Object bindings = scopedValueBindings();
runWith(bindings, task);
}
}
/**
* The VM recognizes this method as special, so any changes to the
* name or signature require corresponding changes in
* JVM_FindScopedValueBindings().
*/
@Hidden
@ForceInline
private void runWith(Object bindings, Runnable op) {
ensureMaterializedForStackWalk(bindings);
op.run();
Reference.reachabilityFence(bindings);
}
/**
* Null out reference after Thread termination.
*/

View file

@ -24,6 +24,7 @@
*/
package java.lang;
import java.lang.ref.Reference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Locale;
@ -53,6 +54,8 @@ import jdk.internal.vm.StackableScope;
import jdk.internal.vm.ThreadContainer;
import jdk.internal.vm.ThreadContainers;
import jdk.internal.vm.annotation.ChangesCurrentThread;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
import jdk.internal.vm.annotation.JvmtiMountTransition;
import sun.nio.ch.Interruptible;
import sun.security.action.GetPropertyAction;
@ -283,13 +286,13 @@ final class VirtualThread extends BaseVirtualThread {
event.commit();
}
Object bindings = scopedValueBindings();
try {
task.run();
runWith(bindings, task);
} catch (Throwable exc) {
dispatchUncaughtException(exc);
} finally {
try {
// pop any remaining scopes from the stack, this may block
StackableScope.popAll();
@ -311,6 +314,14 @@ final class VirtualThread extends BaseVirtualThread {
}
}
@Hidden
@ForceInline
private void runWith(Object bindings, Runnable op) {
ensureMaterializedForStackWalk(bindings);
op.run();
Reference.reachabilityFence(bindings);
}
/**
* Mounts this virtual thread onto the current platform thread. On
* return, the current thread is the virtual thread.
@ -488,8 +499,8 @@ final class VirtualThread extends BaseVirtualThread {
boolean started = false;
container.onStart(this); // may throw
try {
// extent locals may be inherited
inheritExtentLocalBindings(container);
// scoped values may be inherited
inheritScopedValueBindings(container);
// submit task to run thread
submitRunContinuation();

View file

@ -401,7 +401,7 @@ public final class ThreadLocalRandom extends Random {
= new AtomicLong(RandomSupport.mixMurmur64(System.currentTimeMillis()) ^
RandomSupport.mixMurmur64(System.nanoTime()));
// used by ExtentLocal
// used by ScopedValue
private static class Access {
static {
SharedSecrets.setJavaUtilConcurrentTLRAccess(

View file

@ -476,24 +476,28 @@ public interface JavaLangAccess {
boolean isCarrierThreadLocalPresent(CarrierThreadLocal<?> local);
/**
* Returns the current thread's extent locals cache
* Returns the current thread's scoped values cache
*/
Object[] extentLocalCache();
Object[] scopedValueCache();
/**
* Sets the current thread's extent locals cache
* Sets the current thread's scoped values cache
*/
void setExtentLocalCache(Object[] cache);
void setScopedValueCache(Object[] cache);
/**
* Return the current thread's extent local bindings.
* Return the current thread's scoped value bindings.
*/
Object extentLocalBindings();
Object scopedValueBindings();
/**
* Set the current thread's extent local bindings.
* Set the current thread's scoped value bindings.
*/
void setExtentLocalBindings(Object bindings);
void setScopedValueBindings(Object bindings);
Object findScopedValueBindings();
void ensureMaterializedForStackWalk(Object value);
/**
* Returns the innermost mounted continuation

View file

@ -35,7 +35,7 @@ import java.util.concurrent.locks.LockSupport;
import java.util.stream.Stream;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.ExtentLocalContainer;
import jdk.internal.vm.ScopedValueContainer;
import jdk.internal.vm.ThreadContainer;
import jdk.internal.vm.ThreadContainers;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
@ -99,7 +99,7 @@ public class ThreadFlock implements AutoCloseable {
private volatile int threadCount;
private final String name;
private final ExtentLocalContainer.BindingsSnapshot extentLocalBindings;
private final ScopedValueContainer.BindingsSnapshot scopedValueBindings;
private final ThreadContainerImpl container; // encapsulate for now
// state
@ -111,7 +111,7 @@ public class ThreadFlock implements AutoCloseable {
ThreadFlock(String name) {
this.name = name;
this.extentLocalBindings = ExtentLocalContainer.captureBindings();
this.scopedValueBindings = ScopedValueContainer.captureBindings();
this.container = new ThreadContainerImpl(this);
}
@ -119,8 +119,8 @@ public class ThreadFlock implements AutoCloseable {
return threadCount;
}
private ExtentLocalContainer.BindingsSnapshot extentLocalBindings() {
return extentLocalBindings;
private ScopedValueContainer.BindingsSnapshot scopedValueBindings() {
return scopedValueBindings;
}
private void incrementThreadCount() {
@ -210,7 +210,7 @@ public class ThreadFlock implements AutoCloseable {
* Opens a new thread flock. The flock is owned by the current thread. It can be
* named to aid debugging.
*
* <p> This method captures the current thread's {@linkplain ExtentLocal extent-local}
* <p> This method captures the current thread's {@linkplain ScopedValue scoped value}
* bindings for inheritance by threads created in the flock.
*
* <p> For the purposes of containment, monitoring, and debugging, the parent
@ -250,7 +250,7 @@ public class ThreadFlock implements AutoCloseable {
/**
* Starts the given unstarted thread in this flock.
*
* <p> The thread is started with the extent-local bindings that were captured
* <p> The thread is started with the scoped value bindings that were captured
* when opening the flock. The bindings must match the current thread's bindings.
*
* <p> This method may only be invoked by the flock owner or threads {@linkplain
@ -263,7 +263,7 @@ public class ThreadFlock implements AutoCloseable {
* @throws WrongThreadException if the current thread is not the owner or a thread
* contained in the flock
* @throws jdk.incubator.concurrent.StructureViolationException if the current
* extent-local bindings are not the same as when the flock was created
* scoped value bindings are not the same as when the flock was created
*/
public Thread start(Thread thread) {
ensureOwnerOrContainsThread();
@ -398,12 +398,11 @@ public class ThreadFlock implements AutoCloseable {
* <p> A ThreadFlock is intended to be used in a <em>structured manner</em>. If
* this method is called to close a flock before nested flocks are closed then it
* closes the nested flocks (in the reverse order that they were created in),
* closes this flock, and then throws {@link
* jdk.incubator.concurrent.StructureViolationException}.
* Similarly, if called to close a flock that <em>encloses</em> {@linkplain
* jdk.incubator.concurrent.ExtentLocal.Carrier#run(Runnable) operations} with
* extent-local bindings then it also throws {@code StructureViolationException}
* after closing the flock.
* closes this flock, and then throws {@code StructureViolationException}.
* Similarly, if this method is called to close a thread flock while executing with
* scoped value bindings, and the thread flock was created before the scoped values
* were bound, then {@code StructureViolationException} is thrown after closing the
* thread flock.
*
* @throws WrongThreadException if invoked by a thread that is not the owner
* @throws jdk.incubator.concurrent.StructureViolationException if a structure
@ -585,8 +584,8 @@ public class ThreadFlock implements AutoCloseable {
return flock.toString();
}
@Override
public ExtentLocalContainer.BindingsSnapshot extentLocalBindings() {
return flock.extentLocalBindings();
public ScopedValueContainer.BindingsSnapshot scopedValueBindings() {
return flock.scopedValueBindings();
}
}
}

View file

@ -46,7 +46,7 @@ import jdk.internal.access.SharedSecrets;
*/
public class Continuation {
private static final Unsafe U = Unsafe.getUnsafe();
private static final boolean PRESERVE_EXTENT_LOCAL_CACHE;
private static final boolean PRESERVE_SCOPED_VALUE_CACHE;
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
static {
ContinuationSupport.ensureSupported();
@ -54,8 +54,8 @@ public class Continuation {
StackChunk.init(); // ensure StackChunk class is initialized
String value = GetPropertyAction.privilegedGetProperty("jdk.preserveExtentLocalCache");
PRESERVE_EXTENT_LOCAL_CACHE = (value == null) || Boolean.parseBoolean(value);
String value = GetPropertyAction.privilegedGetProperty("jdk.preserveScopedValueCache");
PRESERVE_SCOPED_VALUE_CACHE = (value == null) || Boolean.parseBoolean(value);
}
private static final VarHandle MOUNTED;
@ -129,7 +129,7 @@ public class Continuation {
private Object yieldInfo;
private boolean preempted;
private Object[] extentLocalCache;
private Object[] scopedValueCache;
/**
* Constructs a continuation
@ -238,7 +238,7 @@ public class Continuation {
public final void run() {
while (true) {
mount();
JLA.setExtentLocalCache(extentLocalCache);
JLA.setScopedValueCache(scopedValueCache);
if (done)
throw new IllegalStateException("Continuation terminated");
@ -270,12 +270,12 @@ public class Continuation {
postYieldCleanup();
unmount();
if (PRESERVE_EXTENT_LOCAL_CACHE) {
extentLocalCache = JLA.extentLocalCache();
if (PRESERVE_SCOPED_VALUE_CACHE) {
scopedValueCache = JLA.scopedValueCache();
} else {
extentLocalCache = null;
scopedValueCache = null;
}
JLA.setExtentLocalCache(null);
JLA.setScopedValueCache(null);
} catch (Throwable e) { e.printStackTrace(); System.exit(1); }
}
// we're now in the parent continuation

View file

@ -33,26 +33,26 @@ import jdk.internal.vm.annotation.DontInline;
import jdk.internal.vm.annotation.ReservedStackAccess;
/**
* A StackableScope to represent extent-local bindings.
* A StackableScope to represent scoped-value bindings.
*
* This class defines static methods to run an operation with a ExtentLocalContainer
* on the scope stack. It also defines a method to get the latest ExtentLocalContainer
* and a method to return a snapshot of the extent local bindings.
* This class defines static methods to run an operation with a ScopedValueContainer
* on the scope stack. It also defines a method to get the latest ScopedValueContainer
* and a method to return a snapshot of the scoped value bindings.
*/
public class ExtentLocalContainer extends StackableScope {
public class ScopedValueContainer extends StackableScope {
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
static {
Unsafe.getUnsafe().ensureClassInitialized(StructureViolationExceptions.class);
}
private ExtentLocalContainer() {
private ScopedValueContainer() {
}
/**
* Returns the "latest" ExtentLocalContainer for the current Thread. This may be on
* the current thread's scope task or ma require walking up the tree to find it.
* Returns the "latest" ScopedValueContainer for the current Thread. This may be on
* the current thread's scope task or may require walking up the tree to find it.
*/
public static <T extends ExtentLocalContainer> T latest(Class<T> containerClass) {
public static <T extends ScopedValueContainer> T latest(Class<T> containerClass) {
StackableScope scope = head();
if (scope == null) {
scope = JLA.threadContainer(Thread.currentThread());
@ -69,37 +69,37 @@ public class ExtentLocalContainer extends StackableScope {
}
/**
* Returns the "latest" ExtentLocalContainer for the current Thread. This
* Returns the "latest" ScopedValueContainer for the current Thread. This
* may be on the current thread's scope task or may require walking up the
* tree to find it.
*/
public static ExtentLocalContainer latest() {
return latest(ExtentLocalContainer.class);
public static ScopedValueContainer latest() {
return latest(ScopedValueContainer.class);
}
/**
* A snapshot of the extent local bindings. The snapshot includes the bindings
* established for the current thread and extent local container.
* A snapshot of the scoped value bindings. The snapshot includes the bindings
* established for the current thread and scoped value container.
*/
public record BindingsSnapshot(Object extentLocalBindings,
ExtentLocalContainer container) { }
public record BindingsSnapshot(Object scopedValueBindings,
ScopedValueContainer container) { }
/**
* Returns the extent local bindings for the current thread.
* Returns the scoped value bindings for the current thread.
*/
public static BindingsSnapshot captureBindings() {
return new BindingsSnapshot(JLA.extentLocalBindings(), latest());
return new BindingsSnapshot(JLA.scopedValueBindings(), latest());
}
/**
* For use by ExtentLocal to run an operation in a structured context.
* For use by ScopedValue to run an operation in a structured context.
*/
public static void run(Runnable op) {
if (head() == null) {
// no need to push scope when stack is empty
runWithoutScope(op);
} else {
new ExtentLocalContainer().doRun(op);
new ScopedValueContainer().doRun(op);
}
}
@ -141,14 +141,14 @@ public class ExtentLocalContainer extends StackableScope {
}
/**
* For use by ExtentLocal to call a value returning operation in a structured context.
* For use by ScopedValue to call a value returning operation in a structured context.
*/
public static <V> V call(Callable<V> op) throws Exception {
if (head() == null) {
// no need to push scope when stack is empty
return callWithoutScope(op);
} else {
return new ExtentLocalContainer().doCall(op);
return new ScopedValueContainer().doCall(op);
}
}
@ -199,7 +199,6 @@ public class ExtentLocalContainer extends StackableScope {
* Throws {@code ex} if not null. StructureViolationException is thrown or added
* as a suppressed exception when {@code atTop} is false.
*/
@DontInline @ReservedStackAccess
private static void throwIfFailed(Throwable ex, boolean atTop) {
if (ex != null || !atTop) {
if (!atTop) {

View file

@ -89,9 +89,9 @@ public abstract class ThreadContainer extends StackableScope {
}
/**
* The extent locals captured when the thread container was created.
* The scoped values captured when the thread container was created.
*/
public ExtentLocalContainer.BindingsSnapshot extentLocalBindings() {
public ScopedValueContainer.BindingsSnapshot scopedValueBindings() {
return null;
}
}

View file

@ -167,6 +167,7 @@ module java.base {
jdk.jlink,
jdk.jfr,
jdk.net,
jdk.incubator.concurrent,
jdk.sctp,
jdk.crypto.cryptoki;
exports jdk.internal.foreign to
@ -247,12 +248,14 @@ module java.base {
jdk.unsupported;
exports jdk.internal.vm to
java.management,
jdk.incubator.concurrent,
jdk.internal.jvmstat,
jdk.management,
jdk.management.agent;
exports jdk.internal.vm.annotation to
java.instrument,
jdk.internal.vm.ci,
jdk.incubator.concurrent,
jdk.incubator.vector,
jdk.jfr,
jdk.unsupported;
@ -307,7 +310,8 @@ module java.base {
exports sun.security.action to
java.desktop,
java.security.jgss,
jdk.crypto.ec;
jdk.crypto.ec,
jdk.incubator.concurrent;
exports sun.security.internal.interfaces to
jdk.crypto.cryptoki;
exports sun.security.internal.spec to

View file

@ -50,9 +50,12 @@ static JNINativeMethod methods[] = {
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
{"getStackTrace0", "()" OBJ, (void *)&JVM_GetStackTrace},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
{"extentLocalCache", "()[" OBJ, (void *)&JVM_ExtentLocalCache},
{"setExtentLocalCache", "([" OBJ ")V",(void *)&JVM_SetExtentLocalCache},
{"getNextThreadIdOffset", "()J", (void *)&JVM_GetNextThreadIdOffset}
{"scopedValueCache", "()[" OBJ, (void *)&JVM_ScopedValueCache},
{"setScopedValueCache", "([" OBJ ")V",(void *)&JVM_SetScopedValueCache},
{"getNextThreadIdOffset", "()J", (void *)&JVM_GetNextThreadIdOffset},
{"findScopedValueBindings", "()" OBJ, (void *)&JVM_FindScopedValueBindings},
{"ensureMaterializedForStackWalk",
"(" OBJ ")V", (void*)&JVM_EnsureMaterializedForStackWalk_func},
};
#undef THD