8306647: Implementation of Structured Concurrency (Preview)

8306572: Implementation of Scoped Values (Preview)

Co-authored-by: Alan Bateman <alanb@openjdk.org>
Co-authored-by: Andrew Haley <aph@openjdk.org>
Reviewed-by: psandoz, dfuchs, mchung
This commit is contained in:
Alan Bateman 2023-06-07 06:41:09 +00:00
parent a08c5cb3f1
commit f1c7afcc3f
36 changed files with 3510 additions and 2969 deletions

View File

@ -42,7 +42,6 @@ DOCS_MODULES= \
jdk.hotspot.agent \
jdk.httpserver \
jdk.jpackage \
jdk.incubator.concurrent \
jdk.incubator.vector \
jdk.jartool \
jdk.javadoc \

View File

@ -43,7 +43,6 @@ BOOT_MODULES= \
java.rmi \
java.security.sasl \
java.xml \
jdk.incubator.concurrent \
jdk.incubator.vector \
jdk.internal.vm.ci \
jdk.jfr \

View File

@ -118,6 +118,8 @@
template(java_lang_StringBuilder, "java/lang/StringBuilder") \
template(java_lang_CharSequence, "java/lang/CharSequence") \
template(java_lang_SecurityManager, "java/lang/SecurityManager") \
template(java_lang_ScopedValue, "java/lang/ScopedValue") \
template(java_lang_ScopedValue_Carrier, "java/lang/ScopedValue$Carrier") \
template(java_security_AccessControlContext, "java/security/AccessControlContext") \
template(java_security_AccessController, "java/security/AccessController") \
template(executePrivileged_name, "executePrivileged") \
@ -157,8 +159,6 @@
template(jdk_internal_loader_BuiltinClassLoader, "jdk/internal/loader/BuiltinClassLoader") \
template(jdk_internal_loader_ClassLoaders_AppClassLoader, "jdk/internal/loader/ClassLoaders$AppClassLoader") \
template(jdk_internal_loader_ClassLoaders_PlatformClassLoader, "jdk/internal/loader/ClassLoaders$PlatformClassLoader") \
template(jdk_incubator_concurrent_ScopedValue, "jdk/incubator/concurrent/ScopedValue") \
template(jdk_incubator_concurrent_ScopedValue_Carrier, "jdk/incubator/concurrent/ScopedValue$Carrier") \
\
/* Java runtime version access */ \
template(java_lang_VersionProps, "java/lang/VersionProps") \

View File

@ -1362,7 +1362,7 @@ class ScopedValueBindingsResolver {
public:
InstanceKlass* Carrier_klass;
ScopedValueBindingsResolver(JavaThread* THREAD) {
Klass *k = SystemDictionary::resolve_or_fail(vmSymbols::jdk_incubator_concurrent_ScopedValue_Carrier(), true, THREAD);
Klass *k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_ScopedValue_Carrier(), true, THREAD);
Carrier_klass = InstanceKlass::cast(k);
}
};
@ -1395,7 +1395,7 @@ JVM_ENTRY(jobject, JVM_FindScopedValueBindings(JNIEnv *env, jclass cls))
if (loc != -1) {
javaVFrame *frame = vfst.asJavaVFrame();
StackValueCollection* locals = frame->locals();
StackValue* head_sv = locals->at(loc); // jdk/incubator/concurrent/ScopedValue$Snapshot
StackValue* head_sv = locals->at(loc); // java/lang/ScopedValue$Snapshot
Handle result = head_sv->get_obj();
assert(!head_sv->obj_is_scalar_replaced(), "found scalar-replaced object");
if (result() != nullptr) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -24,109 +24,141 @@
* questions.
*/
package jdk.incubator.concurrent;
package java.lang;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.lang.ref.Reference;
import java.util.concurrent.Callable;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructureViolationException;
import java.util.function.Supplier;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.JavaUtilConcurrentTLRAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
import jdk.internal.vm.annotation.Stable;
import jdk.internal.vm.ScopedValueContainer;
import sun.security.action.GetPropertyAction;
/**
* A value that is set once and is then available for reading for a bounded period of
* execution by a thread. A {@code ScopedValue} allows for safely and efficiently sharing
* data for a bounded period of execution without passing the data as method arguments.
* A value that may be safely and efficiently shared to methods without using method
* parameters.
*
* <p> {@code ScopedValue} defines the {@link #where(ScopedValue, Object, Runnable)}
* method to set the value of a {@code ScopedValue} for the bouned period of execution by
* a thread of the runnable's {@link Runnable#run() run} method. The unfolding execution of
* the methods executed by {@code run} defines a <b><em>dynamic scope</em></b>. The scoped
* value is {@linkplain #isBound() bound} while executing in the dynamic scope, it reverts
* to being <em>unbound</em> when the {@code run} method completes (normally or with an
* exception). Code executing in the dynamic scope uses the {@code ScopedValue} {@link
* #get() get} method to read its value.
* <p> In the Java programming language, data is usually passed to a method by means of a
* method parameter. The data may need to be passed through a sequence of many methods to
* get to the method that makes use of the data. Every method in the sequence of calls
* needs to declare the parameter and every method has access to the data.
* {@code ScopedValue} provides a means to pass data to a faraway method (typically a
* <em>callback</em>) without using method parameters. In effect, a {@code ScopedValue}
* is an <em>implicit method parameter</em>. It is "as if" every method in a sequence of
* calls has an additional parameter. None of the methods declare the parameter and only
* the methods that have access to the {@code ScopedValue} object can access its value
* (the data). {@code ScopedValue} makes it possible to securely pass data from a
* <em>caller</em> to a faraway <em>callee</em> through a sequence of intermediate methods
* that do not declare a parameter for the data and have no access to the data.
*
* <p> Like a {@linkplain ThreadLocal thread-local variable}, a scoped value has multiple
* incarnations, one per thread. The particular incarnation that is used depends on which
* thread calls its methods.
* <p> The {@code ScopedValue} API works by executing a method with a {@code ScopedValue}
* object <em>bound</em> to some value for the bounded period of execution of a method.
* The method may invoke another method, which in turn may invoke another. The unfolding
* execution of the methods define a <em>dynamic scope</em>. Code in these methods with
* access to the {@code ScopedValue} object may read its value. The {@code ScopedValue}
* object reverts to being <em>unbound</em> when the original method completes normally or
* with an exception. The {@code ScopedValue} API supports executing a {@link Runnable#run()
* Runnable.run}, {@link Callable#call() Callable.call}, or {@link Supplier#get() Supplier.get}
* method with a {@code ScopedValue} bound to a value.
*
* <p> Consider the following example with a scoped value {@code USERNAME} that is
* <em>bound</em> to the value "{@code duke}" for the execution, by a thread, of a run
* method that invokes {@code doSomething()}.
* <p> Consider the following example with a scoped value "{@code NAME}" bound to the value
* "{@code duke}" for the execution of a {@code run} method. The {@code run} method, in
* turn, invokes {@code doSomething}.
* {@snippet lang=java :
* // @link substring="newInstance" target="#newInstance" :
* private static final ScopedValue<String> USERNAME = ScopedValue.newInstance();
* private static final ScopedValue<String> NAME = ScopedValue.newInstance();
*
* ScopedValue.where(USERNAME, "duke", () -> doSomething());
* // @link substring="runWhere" target="#runWhere" :
* ScopedValue.runWhere(NAME, "duke", () -> doSomething());
* }
* Code executed directly or indirectly by {@code doSomething()} that invokes {@code
* USERNAME.get()} will read the value "{@code duke}". The scoped value is bound while
* executing {@code doSomething()} and becomes unbound when {@code doSomething()}
* completes (normally or with an exception). If one thread were to call {@code
* doSomething()} with {@code USERNAME} bound to "{@code duke1}", and another thread
* were to call the method with {@code USERNAME} bound to "{@code duke2}", then
* {@code USERNAME.get()} would read the value "{@code duke1}" or "{@code duke2}",
* depending on which thread is executing.
* Code executed directly or indirectly by {@code doSomething}, with access to the field
* {@code NAME}, can invoke {@code NAME.get()} to read the value "{@code duke}". {@code
* NAME} is bound while executing the {@code run} method. It reverts to being unbound when
* the {@code run} method completes.
*
* <p> In addition to the {@code where} method that executes a {@code run} method, {@code
* ScopedValue} defines the {@link #where(ScopedValue, Object, Callable)} method to execute
* a method that returns a result. It also defines the {@link #where(ScopedValue, Object)}
* method for cases where it is useful to accumulate mappings of {@code ScopedValue} to
* value.
* <p> The example using {@code runWhere} invokes a method that does not return a result.
* The {@link #callWhere(ScopedValue, Object, Callable) callWhere} and {@link
* #getWhere(ScopedValue, Object, Supplier) getWhere} can be used to invoke a method that
* returns a result.
* In addition, {@code ScopedValue} defines the {@link #where(ScopedValue, Object)} method
* for cases where multiple mappings (of {@code ScopedValue} to value) are accumulated
* in advance of calling a method with all {@code ScopedValue}s bound to their value.
*
* <p> A {@code ScopedValue} will typically be declared in a {@code final} and {@code
* static} field. The accessibility of the field will determine which components can
* bind or read its value.
* <h2>Bindings are per-thread</h2>
*
* <p> Unless otherwise specified, passing a {@code null} argument to a method in this
* class will cause a {@link NullPointerException} to be thrown.
* A {@code ScopedValue} binding to a value is per-thread. Invoking {@code xxxWhere}
* executes a method with a {@code ScopedValue} bound to a value for the current thread.
* The {@link #get() get} method returns the value bound for the current thread.
*
* <p> In the example, if code executed by one thread invokes this:
* {@snippet lang=java :
* ScopedValue.runWhere(NAME, "duke1", () -> doSomething());
* }
* and code executed by another thread invokes:
* {@snippet lang=java :
* ScopedValue.runWhere(NAME, "duke2", () -> doSomething());
* }
* then code in {@code doSomething} (or any method that it calls) invoking {@code NAME.get()}
* will read the value "{@code duke1}" or "{@code duke2}", depending on which thread is
* executing.
*
* <h2>Scoped values as capabilities</h2>
*
* A {@code ScopedValue} object should be treated as a <em>capability</em> or a key to
* access its value when the {@code ScopedValue} is bound. Secure usage depends on access
* control (see <cite>The Java Virtual Machine Specification</cite>, Section {@jvms 5.4.4})
* and taking care to not share the {@code ScopedValue} object. In many cases, a {@code
* ScopedValue} will be declared in a {@code final} and {@code static} field so that it
* is only accessible to code in a single class (or nest).
*
* <h2><a id="rebind">Rebinding</a></h2>
*
* The {@code ScopedValue} API allows a new binding to be established for <em>nested
* dynamic scopes</em>. This is known as <em>rebinding</em>. A {@code ScopedValue} that
* is bound to some value may be bound to a new value for the bounded execution of some
* is bound to a value may be bound to a new value for the bounded execution of a new
* method. The unfolding execution of code executed by that method defines the nested
* dynamic scope. When the method completes (normally or with an exception), the value of
* the {@code ScopedValue} reverts to its previous value.
* dynamic scope. When the method completes, the value of the {@code ScopedValue} reverts
* to its previous value.
*
* <p> In the above example, suppose that code executed by {@code doSomething()} binds
* {@code USERNAME} to a new value with:
* <p> In the above example, suppose that code executed by {@code doSomething} binds
* {@code NAME} to a new value with:
* {@snippet lang=java :
* ScopedValue.where(USERNAME, "duchess", () -> doMore());
* ScopedValue.runWhere(NAME, "duchess", () -> doMore());
* }
* Code executed directly or indirectly by {@code doMore()} that invokes {@code
* USERNAME.get()} will read the value "{@code duchess}". When {@code doMore()} completes
* (normally or with an exception), the value of {@code USERNAME} reverts to
* "{@code duke}".
* NAME.get()} will read the value "{@code duchess}". When {@code doMore()} completes
* then the value of {@code NAME} reverts to "{@code duke}".
*
* <h2><a id="inheritance">Inheritance</a></h2>
*
* {@code ScopedValue} supports sharing data across threads. This sharing is limited to
* {@code ScopedValue} supports sharing across threads. This sharing is limited to
* structured cases where child threads are started and terminate within the bounded
* period of execution by a parent thread. More specifically, when using a {@link
* StructuredTaskScope}, scoped value bindings are <em>captured</em> when creating a
* {@code StructuredTaskScope} and inherited by all threads started in that scope with
* the {@link StructuredTaskScope#fork(Callable) fork} method.
* period of execution by a parent thread. When using a {@link StructuredTaskScope},
* scoped value bindings are <em>captured</em> when creating a {@code StructuredTaskScope}
* and inherited by all threads started in that task scope with the
* {@link StructuredTaskScope#fork(Callable) fork} method.
*
* <p> In the following example, the {@code ScopedValue} {@code USERNAME} is bound to the
* <p> A {@code ScopedValue} that is shared across threads requires that the value be an
* immutable object or for all access to the value to be appropriately synchronized.
*
* <p> In the following example, the {@code ScopedValue} {@code NAME} is bound to the
* value "{@code duke}" for the execution of a runnable operation. The code in the {@code
* run} method creates a {@code StructuredTaskScope} and forks three child threads. Code
* executed directly or indirectly by these threads running {@code childTask1()},
* {@code childTask2()}, and {@code childTask3()} will read the value "{@code duke}".
* run} method creates a {@code StructuredTaskScope} that forks three tasks. Code executed
* directly or indirectly by these threads running {@code childTask1()}, {@code childTask2()},
* and {@code childTask3()} that invokes {@code NAME.get()} will read the value
* "{@code duke}".
*
* {@snippet lang=java :
* private static final ScopedValue<String> USERNAME = ScopedValue.newInstance();
* private static final ScopedValue<String> NAME = ScopedValue.newInstance();
* ScopedValue.where(USERNAME, "duke", () -> {
* ScopedValue.runWhere(NAME, "duke", () -> {
* try (var scope = new StructuredTaskScope<String>()) {
*
* scope.fork(() -> childTask1());
@ -138,6 +170,23 @@ import sun.security.action.GetPropertyAction;
* });
* }
*
* <p> Unless otherwise specified, passing a {@code null} argument to a method in this
* class will cause a {@link NullPointerException} to be thrown.
*
* @apiNote
* A {@code ScopedValue} should be preferred over a {@link ThreadLocal} for cases where
* the goal is "one-way transmission" of data without using method parameters. While a
* {@code ThreadLocal} can be used to pass data to a method without using method parameters,
* it does suffer from a number of issues:
* <ol>
* <li> {@code ThreadLocal} does not prevent code in a faraway callee from {@linkplain
* ThreadLocal#set(Object) setting} a new value.
* <li> A {@code ThreadLocal} has an unbounded lifetime and thus continues to have a value
* after a method completes, unless explicitly {@linkplain ThreadLocal#remove() removed}.
* <li> {@linkplain InheritableThreadLocal Inheritance} is expensive - the map of
* thread-locals to values must be copied when creating each child thread.
* </ol>
*
* @implNote
* Scoped values are designed to be used in fairly small
* numbers. {@link #get} initially performs a search through enclosing
@ -158,11 +207,11 @@ import sun.security.action.GetPropertyAction;
* it makes sense to create a record class to hold those values, and
* then bind a single {@code ScopedValue} to an instance of that record.
*
* <p>For this incubator release, the reference implementation
* <p>For this release, the reference implementation
* provides some system properties to tune the performance of scoped
* values.
*
* <p>The system property {@code jdk.incubator.concurrent.ScopedValue.cacheSize}
* <p>The system property {@code java.lang.ScopedValue.cacheSize}
* controls the size of the (per-thread) scoped-value cache. This cache is crucial
* for the performance of scoped values. If it is too small,
* the runtime library will repeatedly need to scan for each
@ -171,7 +220,7 @@ import sun.security.action.GetPropertyAction;
* be varied from 2 to 16 entries in size. {@code ScopedValue.cacheSize}
* must be an integer power of 2.
*
* <p>For example, you could use {@code -Djdk.incubator.concurrent.ScopedValue.cacheSize=8}.
* <p>For example, you could use {@code -Djava.lang.ScopedValue.cacheSize=8}.
*
* <p>The other system property is {@code jdk.preserveScopedValueCache}.
* This property determines whether the per-thread scoped-value
@ -184,13 +233,12 @@ import sun.security.action.GetPropertyAction;
* memory saving, but each virtual thread's scoped-value cache would
* have to be regenerated after a blocking operation.
*
* @param <T> the type of the object bound to this {@code ScopedValue}
* @since 20
* @param <T> the type of the value
* @since 21
*/
@PreviewFeature(feature = PreviewFeature.Feature.SCOPED_VALUES)
public final class ScopedValue<T> {
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
private final @Stable int hash;
private final int hash;
@Override
public int hashCode() { return hash; }
@ -243,7 +291,7 @@ public final class ScopedValue<T> {
/**
* A mapping of scoped values, as <em>keys</em>, to values.
*
* <p> A {@code Carrier} is used to accumlate mappings so that an operation (a
* <p> A {@code Carrier} is used to accumulate mappings so that an operation (a
* {@link Runnable} or {@link Callable}) can be executed with all scoped values in the
* mapping bound to values. The following example runs an operation with {@code k1}
* bound (or rebound) to {@code v1}, and {@code k2} bound (or rebound) to {@code v2}.
@ -259,8 +307,9 @@ public final class ScopedValue<T> {
* <p> Unless otherwise specified, passing a {@code null} argument to a method in
* this class will cause a {@link NullPointerException} to be thrown.
*
* @since 20
* @since 21
*/
@PreviewFeature(feature = PreviewFeature.Feature.SCOPED_VALUES)
public static final class Carrier {
// Bit masks: a 1 in postion n indicates that this set of bound values
// hits that slot in the cache.
@ -283,8 +332,7 @@ public final class ScopedValue<T> {
/**
* Add a binding to this map, returning a new Carrier instance.
*/
private static final <T> Carrier where(ScopedValue<T> key, T value,
Carrier prev) {
private static <T> Carrier where(ScopedValue<T> key, T value, Carrier prev) {
return new Carrier(key, value, prev);
}
@ -311,11 +359,11 @@ public final class ScopedValue<T> {
return where(key, value, null);
}
final Object get() {
Object get() {
return value;
}
final ScopedValue<?> getKey() {
ScopedValue<?> getKey() {
return key;
}
@ -360,7 +408,7 @@ public final class ScopedValue<T> {
* @param <R> the type of the result of the operation
* @return the result
* @throws Exception if {@code op} completes with an exception
* @see ScopedValue#where(ScopedValue, Object, Callable)
* @see ScopedValue#callWhere(ScopedValue, Object, Callable)
*/
public <R> R call(Callable<? extends R> op) throws Exception {
Objects.requireNonNull(op);
@ -370,6 +418,48 @@ public final class ScopedValue<T> {
return runWith(newSnapshot, op);
}
/**
* Invokes a supplier of results with each scoped value in this mapping bound
* to its value in the current thread.
* When the operation completes (normally or with an exception), each scoped value
* in the mapping will revert to being unbound, or revert to its previous value
* when previously bound, in the current thread.
*
* <p> Scoped values are intended to be used in a <em>structured manner</em>.
* If {@code op} creates a {@link StructuredTaskScope} but does not {@linkplain
* StructuredTaskScope#close() close} it, then exiting {@code op} causes the
* underlying construct of each {@code StructuredTaskScope} created in the
* dynamic scope to be closed. This may require blocking until all child threads
* have completed their sub-tasks. The closing is done in the reverse order that
* they were created. Once closed, {@link StructureViolationException} is thrown.
*
* @param op the operation to run
* @param <R> the type of the result of the operation
* @return the result
* @see ScopedValue#getWhere(ScopedValue, Object, Supplier)
*/
public <R> R get(Supplier<? extends R> op) {
Objects.requireNonNull(op);
Cache.invalidate(bitmask);
var prevSnapshot = scopedValueBindings();
var newSnapshot = new Snapshot(this, prevSnapshot);
return runWith(newSnapshot, new CallableAdapter<R>(op));
}
// A lightweight adapter from Supplier to Callable. This is
// used here to create the Callable which is passed to
// Carrier#call() in this thread because it needs neither
// runtime bytecode generation nor any release fencing.
private static final class CallableAdapter<V> implements Callable<V> {
private /*non-final*/ Supplier<? extends V> s;
CallableAdapter(Supplier<? extends V> s) {
this.s = s;
}
public V call() {
return s.get();
}
}
/**
* Execute the action with a set of ScopedValue bindings.
*
@ -379,14 +469,14 @@ public final class ScopedValue<T> {
*/
@Hidden
@ForceInline
private <R> R runWith(Snapshot newSnapshot, Callable<R> op) throws Exception {
private <R> R runWith(Snapshot newSnapshot, Callable<R> op) {
try {
JLA.setScopedValueBindings(newSnapshot);
JLA.ensureMaterializedForStackWalk(newSnapshot);
Thread.setScopedValueBindings(newSnapshot);
Thread.ensureMaterializedForStackWalk(newSnapshot);
return ScopedValueContainer.call(op);
} finally {
Reference.reachabilityFence(newSnapshot);
JLA.setScopedValueBindings(newSnapshot.prev);
Thread.setScopedValueBindings(newSnapshot.prev);
Cache.invalidate(bitmask);
}
}
@ -407,7 +497,7 @@ public final class ScopedValue<T> {
* they were created. Once closed, {@link StructureViolationException} is thrown.
*
* @param op the operation to run
* @see ScopedValue#where(ScopedValue, Object, Runnable)
* @see ScopedValue#runWhere(ScopedValue, Object, Runnable)
*/
public void run(Runnable op) {
Objects.requireNonNull(op);
@ -428,12 +518,12 @@ public final class ScopedValue<T> {
@ForceInline
private void runWith(Snapshot newSnapshot, Runnable op) {
try {
JLA.setScopedValueBindings(newSnapshot);
JLA.ensureMaterializedForStackWalk(newSnapshot);
Thread.setScopedValueBindings(newSnapshot);
Thread.ensureMaterializedForStackWalk(newSnapshot);
ScopedValueContainer.run(op);
} finally {
Reference.reachabilityFence(newSnapshot);
JLA.setScopedValueBindings(newSnapshot.prev);
Thread.setScopedValueBindings(newSnapshot.prev);
Cache.invalidate(bitmask);
}
}
@ -488,12 +578,46 @@ public final class ScopedValue<T> {
* @return the result
* @throws Exception if the operation completes with an exception
*/
public static <T, R> R where(ScopedValue<T> key,
T value,
Callable<? extends R> op) throws Exception {
public static <T, R> R callWhere(ScopedValue<T> key,
T value,
Callable<? extends R> op) throws Exception {
return where(key, value).call(op);
}
/**
* Invokes a supplier of results with a {@code ScopedValue} bound to a value
* in the current thread. When the operation completes (normally or with an
* exception), the {@code ScopedValue} will revert to being unbound, or revert to
* its previous value when previously bound, in the current thread.
*
* <p> Scoped values are intended to be used in a <em>structured manner</em>.
* If {@code op} creates a {@link StructuredTaskScope} but does not {@linkplain
* StructuredTaskScope#close() close} it, then exiting {@code op} causes the
* underlying construct of each {@code StructuredTaskScope} created in the
* dynamic scope to be closed. This may require blocking until all child threads
* have completed their sub-tasks. The closing is done in the reverse order that
* they were created. Once closed, {@link StructureViolationException} is thrown.
*
* @implNote
* This method is implemented to be equivalent to:
* {@snippet lang=java :
* // @link substring="get" target="Carrier#get(Supplier)" :
* ScopedValue.where(key, value).get(op);
* }
*
* @param key the {@code ScopedValue} key
* @param value the value, can be {@code null}
* @param <T> the type of the value
* @param <R> the result type
* @param op the operation to call
* @return the result
*/
public static <T, R> R getWhere(ScopedValue<T> key,
T value,
Supplier<? extends R> op) {
return where(key, value).get(op);
}
/**
* Run an operation with a {@code ScopedValue} bound to a value in the current
* thread. When the operation completes (normally or with an exception), the
@ -520,7 +644,7 @@ public final class ScopedValue<T> {
* @param <T> the type of the value
* @param op the operation to call
*/
public static <T> void where(ScopedValue<T> key, T value, Runnable op) {
public static <T> void runWhere(ScopedValue<T> key, T value, Runnable op) {
where(key, value).run(op);
}
@ -642,11 +766,11 @@ public final class ScopedValue<T> {
}
private static Object[] scopedValueCache() {
return JLA.scopedValueCache();
return Thread.scopedValueCache();
}
private static void setScopedValueCache(Object[] cache) {
JLA.setScopedValueCache(cache);
Thread.setScopedValueCache(cache);
}
// Special value to indicate this is a newly-created Thread
@ -662,24 +786,24 @@ public final class ScopedValue<T> {
// 3: A Snapshot instance: this contains one or more scoped value
// bindings.
// 4: null: there may be some bindings in this Thread, but we don't know
// where they are. We must invoke JLA.findScopedValueBindings() to walk
// where they are. We must invoke Thread.findScopedValueBindings() to walk
// the stack to find them.
Object bindings = JLA.scopedValueBindings();
Object bindings = Thread.scopedValueBindings();
if (bindings == NEW_THREAD_BINDINGS) {
// This must be a new thread
return Snapshot.EMPTY_SNAPSHOT;
}
if (bindings == null) {
// Search the stack
bindings = JLA.findScopedValueBindings();
bindings = Thread.findScopedValueBindings();
if (bindings == null) {
// Nothing on the stack.
bindings = Snapshot.EMPTY_SNAPSHOT;
}
}
assert (bindings != null);
JLA.setScopedValueBindings(bindings);
Thread.setScopedValueBindings(bindings);
return (Snapshot) bindings;
}
@ -732,7 +856,7 @@ public final class ScopedValue<T> {
private static final int MAX_CACHE_SIZE = 16;
static {
final String propertyName = "jdk.incubator.concurrent.ScopedValue.cacheSize";
final String propertyName = "java.lang.ScopedValue.cacheSize";
var sizeString = GetPropertyAction.privilegedGetProperty(propertyName, "16");
var cacheSize = Integer.valueOf(sizeString);
if (cacheSize < 2 || cacheSize > MAX_CACHE_SIZE) {

View File

@ -2621,19 +2621,6 @@ public final class System {
return Thread.scopedValueBindings();
}
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) {
return thread.getContinuation();
}

View File

@ -37,9 +37,9 @@ import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.StructureViolationException;
import java.util.concurrent.locks.LockSupport;
import jdk.internal.event.ThreadSleepEvent;
import jdk.internal.misc.StructureViolationExceptions;
import jdk.internal.misc.TerminatingThreadLocal;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
@ -321,7 +321,7 @@ public class Thread implements Runnable {
// bindings established for running/calling an operation
Object bindings = snapshot.scopedValueBindings();
if (currentThread().scopedValueBindings != bindings) {
StructureViolationExceptions.throwException("Scoped value bindings have changed");
throw new StructureViolationException("Scoped value bindings have changed");
}
this.scopedValueBindings = bindings;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2023, 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
@ -22,15 +22,18 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.incubator.concurrent;
package java.util.concurrent;
import jdk.internal.javac.PreviewFeature;
/**
* Thrown when a structure violation is detected.
*
* @see StructuredTaskScope#close()
*
* @since 19
* @since 21
*/
@PreviewFeature(feature = PreviewFeature.Feature.STRUCTURED_CONCURRENCY)
public final class StructureViolationException extends RuntimeException {
@java.io.Serial
private static final long serialVersionUID = -7705327650798235468L;

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,7 @@ class ThreadPerTaskExecutor extends ThreadContainer implements ExecutorService {
MethodHandles.Lookup l = MethodHandles.lookup();
STATE = l.findVarHandle(ThreadPerTaskExecutor.class, "state", int.class);
} catch (Exception e) {
throw new InternalError(e);
throw new ExceptionInInitializerError(e);
}
}

View File

@ -531,15 +531,6 @@ public interface JavaLangAccess {
*/
Object scopedValueBindings();
/**
* Set the current thread's scoped value bindings.
*/
void setScopedValueBindings(Object bindings);
Object findScopedValueBindings();
void ensureMaterializedForStackWalk(Object value);
/**
* Returns the innermost mounted continuation
*/

View File

@ -74,6 +74,10 @@ public @interface PreviewFeature {
UNNAMED,
@JEP(number=445, title="Unnamed Classes and Instance Main Methods")
UNNAMED_CLASSES,
@JEP(number=446, title="Scoped Values", status="Preview")
SCOPED_VALUES,
@JEP(number=453, title="Structured Concurrency", status="Preview")
STRUCTURED_CONCURRENCY,
/**
* A key for testing.
*/

View File

@ -1,90 +0,0 @@
/*
* Copyright (c) 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
* 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 jdk.internal.misc;
import java.lang.reflect.Constructor;
/**
* Defines static methods to allow code in java.base to create or throw
* StructureViolationException. This class will go away when the exception
* moves to java.base.
*/
public class StructureViolationExceptions {
private static final Constructor<?> SVE_CTOR = structureViolationExceptionCtor();
private StructureViolationExceptions() { }
/**
* Creates a StructureViolationException with the given message.
*/
public static RuntimeException newException(String message) {
if (SVE_CTOR != null) {
try {
return (RuntimeException) SVE_CTOR.newInstance(message);
} catch (Exception e) {
throw new InternalError(e);
}
} else {
return new RuntimeException("Structure violation exception: " + message);
}
}
/**
* Creates a StructureViolationException with no message.
*/
public static RuntimeException newException() {
return newException(null);
}
/**
* Throws a StructureViolationException with the given message.
*/
public static void throwException(String message) {
throw newException(message);
}
/**
* Throws a StructureViolationException with no message.
*/
public static void throwException() {
throw newException(null);
}
/**
* Returns the StructureViolationException(String) constructor.
*/
private static Constructor<?> structureViolationExceptionCtor() {
Constructor<?> ctor;
try {
Class<?> exClass = Class.forName("jdk.incubator.concurrent.StructureViolationException");
ctor = exClass.getConstructor(String.class);
} catch (ClassNotFoundException e) {
ctor = null;
} catch (Exception e) {
throw new InternalError(e);
}
return ctor;
}
}

View File

@ -31,6 +31,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.StructureViolationException;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Stream;
import jdk.internal.access.JavaLangAccess;
@ -87,7 +88,6 @@ public class ThreadFlock implements AutoCloseable {
MethodHandles.Lookup l = MethodHandles.lookup();
THREAD_COUNT = l.findVarHandle(ThreadFlock.class, "threadCount", int.class);
PERMIT = l.findVarHandle(ThreadFlock.class, "permit", boolean.class);
Unsafe.getUnsafe().ensureClassInitialized(StructureViolationExceptions.class);
} catch (Exception e) {
throw new InternalError(e);
}
@ -262,8 +262,8 @@ public class ThreadFlock implements AutoCloseable {
* @throws IllegalThreadStateException if the given thread was already started
* @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
* scoped value bindings are not the same as when the flock was created
* @throws StructureViolationException if the current scoped value bindings are
* not the same as when the flock was created
*/
public Thread start(Thread thread) {
ensureOwnerOrContainsThread();
@ -405,8 +405,7 @@ public class ThreadFlock implements AutoCloseable {
* thread flock.
*
* @throws WrongThreadException if invoked by a thread that is not the owner
* @throws jdk.incubator.concurrent.StructureViolationException if a structure
* violation was detected
* @throws StructureViolationException if a structure violation was detected
*/
public void close() {
ensureOwner();
@ -513,14 +512,15 @@ public class ThreadFlock implements AutoCloseable {
@Override
public ThreadContainerImpl push() {
// Virtual threads in the root containers are not tracked so need
// Virtual threads in the root containers may not be tracked so need
// to register container to ensure that it is found
Thread thread = Thread.currentThread();
if (thread.isVirtual()
&& JLA.threadContainer(thread) == ThreadContainers.root()) {
this.key = ThreadContainers.registerContainer(this);
if (!ThreadContainers.trackAllThreads()) {
Thread thread = Thread.currentThread();
if (thread.isVirtual()
&& JLA.threadContainer(thread) == ThreadContainers.root()) {
this.key = ThreadContainers.registerContainer(this);
}
}
super.push();
return this;
}
@ -538,7 +538,7 @@ public class ThreadFlock implements AutoCloseable {
if (key != null)
ThreadContainers.deregisterContainer(key);
if (!atTop)
StructureViolationExceptions.throwException();
throw new StructureViolationException();
}
}
@ -563,6 +563,10 @@ public class ThreadFlock implements AutoCloseable {
}
}
@Override
public String name() {
return flock.name();
}
@Override
public long threadCount() {
return flock.threadCount();
@ -580,10 +584,6 @@ public class ThreadFlock implements AutoCloseable {
flock.onExit(thread);
}
@Override
public String toString() {
return flock.toString();
}
@Override
public ScopedValueContainer.BindingsSnapshot scopedValueBindings() {
return flock.scopedValueBindings();
}

View File

@ -25,12 +25,10 @@
package jdk.internal.vm;
import java.util.concurrent.Callable;
import java.util.concurrent.StructureViolationException;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.StructureViolationExceptions;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.DontInline;
import jdk.internal.vm.annotation.ReservedStackAccess;
/**
* A StackableScope to represent scoped-value bindings.
@ -42,7 +40,7 @@ import jdk.internal.vm.annotation.ReservedStackAccess;
public class ScopedValueContainer extends StackableScope {
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
static {
Unsafe.getUnsafe().ensureClassInitialized(StructureViolationExceptions.class);
Unsafe.getUnsafe().ensureClassInitialized(StructureViolationException.class);
}
private ScopedValueContainer() {
@ -143,7 +141,7 @@ public class ScopedValueContainer extends StackableScope {
/**
* For use by ScopedValue to call a value returning operation in a structured context.
*/
public static <V> V call(Callable<V> op) throws Exception {
public static <V> V call(Callable<V> op) {
if (head() == null) {
// no need to push scope when stack is empty
return callWithoutScope(op);
@ -202,7 +200,7 @@ public class ScopedValueContainer extends StackableScope {
private static void throwIfFailed(Throwable ex, boolean atTop) {
if (ex != null || !atTop) {
if (!atTop) {
var sve = StructureViolationExceptions.newException();
var sve = new StructureViolationException();
if (ex == null) {
ex = sve;
} else {

View File

@ -26,7 +26,6 @@ package jdk.internal.vm;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
@ -95,6 +94,11 @@ public class SharedThreadContainer extends ThreadContainer implements AutoClosea
return create(ThreadContainers.root(), name);
}
@Override
public String name() {
return name;
}
@Override
public Thread owner() {
return null;
@ -159,14 +163,4 @@ public class SharedThreadContainer extends ThreadContainer implements AutoClosea
ThreadContainers.deregisterContainer(key);
}
}
@Override
public String toString() {
String id = Objects.toIdentityString(this);
if (name != null) {
return name + "/" + id;
} else {
return id;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2023, 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
@ -25,6 +25,7 @@
package jdk.internal.vm;
import java.util.Objects;
import java.util.stream.Stream;
/**
@ -41,6 +42,13 @@ public abstract class ThreadContainer extends StackableScope {
super(shared);
}
/**
* Return the name of this container, may be null.
*/
public String name() {
return null;
}
/**
* Returns the parent of this container or null if this is the root container.
*/
@ -94,4 +102,15 @@ public abstract class ThreadContainer extends StackableScope {
public ScopedValueContainer.BindingsSnapshot scopedValueBindings() {
return null;
}
@Override
public String toString() {
String name = name();
if (name != null && name.indexOf('@') >= 0) {
return name;
} else {
String id = Objects.toIdentityString(this);
return (name != null) ? name + "/" + id : id;
}
}
}

View File

@ -217,13 +217,17 @@ public class ThreadContainers {
return null;
}
@Override
public String toString() {
public String name() {
return "<root>";
}
@Override
public StackableScope previous() {
return null;
}
@Override
public String toString() {
return name();
}
/**
* Returns the platform threads that are not in the container as these

View File

@ -162,7 +162,6 @@ module java.base {
jdk.jlink,
jdk.jfr,
jdk.net,
jdk.incubator.concurrent,
jdk.sctp,
jdk.crypto.cryptoki;
exports jdk.internal.foreign to
@ -222,7 +221,6 @@ module java.base {
jdk.charsets,
jdk.compiler,
jdk.crypto.cryptoki,
jdk.incubator.concurrent,
jdk.incubator.vector,
jdk.jfr,
jdk.jshell,
@ -256,7 +254,6 @@ module java.base {
jdk.unsupported;
exports jdk.internal.vm to
java.management,
jdk.incubator.concurrent,
jdk.internal.jvmstat,
jdk.management,
jdk.management.agent,
@ -264,7 +261,6 @@ module java.base {
exports jdk.internal.vm.annotation to
java.instrument,
jdk.internal.vm.ci,
jdk.incubator.concurrent,
jdk.incubator.vector,
jdk.jfr,
jdk.unsupported;
@ -326,8 +322,7 @@ module java.base {
exports sun.security.action to
java.desktop,
java.security.jgss,
jdk.crypto.ec,
jdk.incubator.concurrent;
jdk.crypto.ec;
exports sun.security.internal.interfaces to
jdk.crypto.cryptoki;
exports sun.security.internal.spec to

View File

@ -1,30 +0,0 @@
/*
* Copyright (c) 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
* 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.
*/
/**
* Defines non-final APIs for concurrent programming.
* {@Incubating}
*/
package jdk.incubator.concurrent;

View File

@ -1,35 +0,0 @@
/*
* Copyright (c) 2022, 2023, 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.
*/
/**
* Defines non-final APIs for concurrent programming.
* {@Incubating}
*
* @moduleGraph
*/
module jdk.incubator.concurrent {
exports jdk.incubator.concurrent;
}

View File

@ -487,6 +487,8 @@ java/lang/invoke/LFCaching/LFGarbageCollectedTest.java 8078602 generic-
java/lang/invoke/lambda/LambdaFileEncodingSerialization.java 8249079 linux-x64
java/lang/invoke/RicochetTest.java 8251969 generic-all
java/lang/ScopedValue/StressStackOverflow.java 8303498 linux-s390x
############################################################################
# jdk_instrument
@ -739,9 +741,6 @@ sun/tools/jhsdb/JStackStressTest.java 8276210 linux-aa
javax/rmi/ssl/SSLSocketParametersTest.sh 8162906 generic-all
jdk/incubator/concurrent/ScopedValue/StressStackOverflow.java 8303498 linux-s390x
jdk/incubator/vector/ShortMaxVectorTests.java 8306592 generic-i586
jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows-x64

View File

@ -279,7 +279,6 @@ jdk_loom = \
java/util/concurrent \
java/net/vthread \
java/nio/channels/vthread \
jdk/incubator/concurrent \
jdk/internal/misc/ThreadFlock \
jdk/internal/vm/Continuation \
jdk/jfr/threading
@ -319,7 +318,6 @@ jdk_other = \
javax/xml \
-javax/xml/crypto \
jdk/dynalink \
jdk/incubator/concurrent \
jdk/internal/jline \
com/sun/jndi \
lib/testlibrary

View File

@ -23,15 +23,14 @@
/*
* @test
* @summary Stress test ScopedValue with many bindings and rebinings
* @modules jdk.incubator.concurrent
* @summary Stress test ScopedValue with many bindings and rebindings
* @enablePreview
* @library /test/lib
* @key randomness
* @run junit ManyBindings
*/
import jdk.incubator.concurrent.ScopedValue;
import jdk.incubator.concurrent.ScopedValue.Carrier;
import java.lang.ScopedValue.Carrier;
import java.util.Arrays;
import java.util.Objects;
import java.util.Random;
@ -129,7 +128,7 @@ class ManyBindings {
test(newArray, depth+1);
});
// check that the scoped values have the origina values
// check that the scoped values have the original values
check(array);
}

View File

@ -24,16 +24,16 @@
/*
* @test
* @summary Test ScopedValue API
* @modules jdk.incubator.concurrent
* @run junit ScopeValueAPI
* @enablePreview
* @run junit ScopedValueAPI
*/
import jdk.incubator.concurrent.ScopedValue;
import java.util.NoSuchElementException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
@ -41,7 +41,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
class ScopeValueAPI {
class ScopedValueAPI {
private static Stream<ThreadFactory> factories() {
return Stream.of(Thread.ofPlatform().factory(), Thread.ofVirtual().factory());
@ -56,7 +56,7 @@ class ScopeValueAPI {
test(factory, () -> {
class Box { static boolean executed; }
ScopedValue<String> name = ScopedValue.newInstance();
ScopedValue.where(name, "duke", () -> { Box.executed = true; });
ScopedValue.runWhere(name, "duke", () -> { Box.executed = true; });
assertTrue(Box.executed);
});
}
@ -71,7 +71,7 @@ class ScopeValueAPI {
class FooException extends RuntimeException { }
ScopedValue<String> name = ScopedValue.newInstance();
Runnable op = () -> { throw new FooException(); };
assertThrows(FooException.class, () -> ScopedValue.where(name, "duke", op));
assertThrows(FooException.class, () -> ScopedValue.runWhere(name, "duke", op));
assertFalse(name.isBound());
});
}
@ -84,7 +84,20 @@ class ScopeValueAPI {
void testCall(ThreadFactory factory) throws Exception {
test(factory, () -> {
ScopedValue<String> name = ScopedValue.newInstance();
String result = ScopedValue.where(name, "duke", name::get);
String result = ScopedValue.callWhere(name, "duke", name::get);
assertEquals("duke", result);
});
}
/**
* Test that the get method is invoked.
*/
@ParameterizedTest
@MethodSource("factories")
void testGetWhere(ThreadFactory factory) throws Exception {
test(factory, () -> {
ScopedValue<String> name = ScopedValue.newInstance();
String result = ScopedValue.getWhere(name, "duke", (Supplier<String>)(name::get));
assertEquals("duke", result);
});
}
@ -99,7 +112,22 @@ class ScopeValueAPI {
class FooException extends RuntimeException { }
ScopedValue<String> name = ScopedValue.newInstance();
Callable<Void> op = () -> { throw new FooException(); };
assertThrows(FooException.class, () -> ScopedValue.where(name, "duke", op));
assertThrows(FooException.class, () -> ScopedValue.callWhere(name, "duke", op));
assertFalse(name.isBound());
});
}
/**
* Test the get(Supplier) method throwing an exception.
*/
@ParameterizedTest
@MethodSource("factories")
void testGetThrows(ThreadFactory factory) throws Exception {
test(factory, () -> {
class FooException extends RuntimeException { }
ScopedValue<String> name = ScopedValue.newInstance();
Supplier<Void> op = () -> { throw new FooException(); };
assertThrows(FooException.class, () -> ScopedValue.getWhere(name, "duke", op));
assertFalse(name.isBound());
});
}
@ -117,7 +145,7 @@ class ScopeValueAPI {
assertThrows(NoSuchElementException.class, name2::get);
// run
ScopedValue.where(name1, "duke", () -> {
ScopedValue.runWhere(name1, "duke", () -> {
assertEquals("duke", name1.get());
assertThrows(NoSuchElementException.class, name2::get);
@ -126,7 +154,16 @@ class ScopeValueAPI {
assertThrows(NoSuchElementException.class, name2::get);
// call
ScopedValue.where(name1, "duke", () -> {
ScopedValue.callWhere(name1, "duke", () -> {
assertEquals("duke", name1.get());
assertThrows(NoSuchElementException.class, name2::get);
return null;
});
assertThrows(NoSuchElementException.class, name1::get);
assertThrows(NoSuchElementException.class, name2::get);
// get
ScopedValue.getWhere(name1, "duke", () -> {
assertEquals("duke", name1.get());
assertThrows(NoSuchElementException.class, name2::get);
return null;
@ -149,7 +186,7 @@ class ScopeValueAPI {
assertFalse(name2.isBound());
// run
ScopedValue.where(name1, "duke", () -> {
ScopedValue.runWhere(name1, "duke", () -> {
assertTrue(name1.isBound());
assertFalse(name2.isBound());
});
@ -157,7 +194,16 @@ class ScopeValueAPI {
assertFalse(name2.isBound());
// call
ScopedValue.where(name1, "duke", () -> {
ScopedValue.callWhere(name1, "duke", () -> {
assertTrue(name1.isBound());
assertFalse(name2.isBound());
return null;
});
assertFalse(name1.isBound());
assertFalse(name2.isBound());
// call
ScopedValue.callWhere(name1, "duke", () -> {
assertTrue(name1.isBound());
assertFalse(name2.isBound());
return null;
@ -179,13 +225,13 @@ class ScopeValueAPI {
assertEquals("default", name.orElse("default"));
// run
ScopedValue.where(name, "duke", () -> {
ScopedValue.runWhere(name, "duke", () -> {
assertEquals("duke", name.orElse(null));
assertEquals("duke", name.orElse("default"));
});
// call
ScopedValue.where(name, "duke", () -> {
ScopedValue.callWhere(name, "duke", () -> {
assertEquals("duke", name.orElse(null));
assertEquals("duke", name.orElse("default"));
return null;
@ -205,12 +251,12 @@ class ScopeValueAPI {
assertThrows(FooException.class, () -> name.orElseThrow(FooException::new));
// run
ScopedValue.where(name, "duke", () -> {
ScopedValue.runWhere(name, "duke", () -> {
assertEquals("duke", name.orElseThrow(FooException::new));
});
// call
ScopedValue.where(name, "duke", () -> {
ScopedValue.callWhere(name, "duke", () -> {
assertEquals("duke", name.orElseThrow(FooException::new));
return null;
});
@ -248,6 +294,17 @@ class ScopeValueAPI {
assertFalse(name.isBound());
assertFalse(age.isBound());
// get
ScopedValue.where(name, "duke").where(age, 100).get(() -> {
assertTrue(name.isBound());
assertTrue(age.isBound());
assertEquals("duke", name.get());
assertEquals(100, (int) age.get());
return null;
});
assertFalse(name.isBound());
assertFalse(age.isBound());
});
}
@ -261,11 +318,11 @@ class ScopeValueAPI {
ScopedValue<String> name = ScopedValue.newInstance();
// run
ScopedValue.where(name, "duke", () -> {
ScopedValue.runWhere(name, "duke", () -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
ScopedValue.where(name, "duchess", () -> {
ScopedValue.runWhere(name, "duchess", () -> {
assertTrue(name.isBound());
assertEquals("duchess", name.get());
});
@ -276,11 +333,28 @@ class ScopeValueAPI {
assertFalse(name.isBound());
// call
ScopedValue.where(name, "duke", () -> {
ScopedValue.callWhere(name, "duke", () -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
ScopedValue.where(name, "duchess", () -> {
ScopedValue.callWhere(name, "duchess", () -> {
assertTrue(name.isBound());
assertEquals("duchess", name.get());
return null;
});
assertTrue(name.isBound());
assertEquals("duke", name.get());
return null;
});
assertFalse(name.isBound());
// get
ScopedValue.getWhere(name, "duke", () -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
ScopedValue.where(name, "duchess").get(() -> {
assertTrue(name.isBound());
assertEquals("duchess", name.get());
return null;
@ -304,11 +378,11 @@ class ScopeValueAPI {
ScopedValue<String> name = ScopedValue.newInstance();
// run
ScopedValue.where(name, null, () -> {
ScopedValue.runWhere(name, null, () -> {
assertTrue(name.isBound());
assertNull(name.get());
ScopedValue.where(name, "duchess", () -> {
ScopedValue.runWhere(name, "duchess", () -> {
assertTrue(name.isBound());
assertTrue("duchess".equals(name.get()));
});
@ -319,11 +393,28 @@ class ScopeValueAPI {
assertFalse(name.isBound());
// call
ScopedValue.where(name, null, () -> {
ScopedValue.callWhere(name, null, () -> {
assertTrue(name.isBound());
assertNull(name.get());
ScopedValue.where(name, "duchess", () -> {
ScopedValue.callWhere(name, "duchess", () -> {
assertTrue(name.isBound());
assertTrue("duchess".equals(name.get()));
return null;
});
assertTrue(name.isBound());
assertNull(name.get());
return null;
});
assertFalse(name.isBound());
// getWhere
ScopedValue.getWhere(name, null, () -> {
assertTrue(name.isBound());
assertNull(name.get());
ScopedValue.getWhere(name, "duchess", () -> {
assertTrue(name.isBound());
assertTrue("duchess".equals(name.get()));
return null;
@ -347,11 +438,11 @@ class ScopeValueAPI {
ScopedValue<String> name = ScopedValue.newInstance();
// run
ScopedValue.where(name, "duke", () -> {
ScopedValue.runWhere(name, "duke", () -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
ScopedValue.where(name, null, () -> {
ScopedValue.runWhere(name, null, () -> {
assertTrue(name.isBound());
assertNull(name.get());
});
@ -362,11 +453,28 @@ class ScopeValueAPI {
assertFalse(name.isBound());
// call
ScopedValue.where(name, "duke", () -> {
ScopedValue.callWhere(name, "duke", () -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
ScopedValue.where(name, null, () -> {
ScopedValue.callWhere(name, null, () -> {
assertTrue(name.isBound());
assertNull(name.get());
return null;
});
assertTrue(name.isBound());
assertEquals("duke", name.get());
return null;
});
assertFalse(name.isBound());
// get
ScopedValue.where(name, "duke").get(() -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
ScopedValue.where(name, null).get(() -> {
assertTrue(name.isBound());
assertNull(name.get());
return null;
@ -410,14 +518,15 @@ class ScopeValueAPI {
ScopedValue<String> name = ScopedValue.newInstance();
assertThrows(NullPointerException.class, () -> ScopedValue.where(null, "value"));
assertThrows(NullPointerException.class, () -> ScopedValue.where(null, "value", () -> { }));
assertThrows(NullPointerException.class, () -> ScopedValue.where(null, "value", () -> null));
assertThrows(NullPointerException.class, () -> ScopedValue.runWhere(null, "value", () -> { }));
assertThrows(NullPointerException.class, () -> ScopedValue.getWhere(null, "value", () -> null));
assertThrows(NullPointerException.class, () -> name.orElseThrow(null));
var carrier = ScopedValue.where(name, "duke");
assertThrows(NullPointerException.class, () -> carrier.where(null, "value"));
assertThrows(NullPointerException.class, () -> carrier.get(null));
assertThrows(NullPointerException.class, () -> carrier.get((ScopedValue<?>)null));
assertThrows(NullPointerException.class, () -> carrier.get((Supplier<?>)null));
assertThrows(NullPointerException.class, () -> carrier.run(null));
assertThrows(NullPointerException.class, () -> carrier.call(null));
}

View File

@ -24,8 +24,7 @@
/**
* @test
* @summary StressStackOverflow the recovery path for ScopedValue
* @modules jdk.incubator.concurrent
* @compile StressStackOverflow.java
* @enablePreview
* @run main/othervm/timeout=300 -XX:-TieredCompilation StressStackOverflow
* @run main/othervm/timeout=300 -XX:TieredStopAtLevel=1 StressStackOverflow
* @run main/othervm/timeout=300 StressStackOverflow
@ -34,9 +33,9 @@
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;
import jdk.incubator.concurrent.ScopedValue;
import jdk.incubator.concurrent.StructureViolationException;
import jdk.incubator.concurrent.StructuredTaskScope;
import java.util.concurrent.StructureViolationException;
import java.util.concurrent.StructuredTaskScope;
import java.util.function.Supplier;
public class StressStackOverflow {
public static final ScopedValue<Integer> el = ScopedValue.newInstance();
@ -53,9 +52,16 @@ public class StressStackOverflow {
// Test the ScopedValue recovery mechanism for stack overflows. We implement both Callable
// and Runnable interfaces. Which one gets tested depends on the constructor argument.
class DeepRecursion implements Callable, Runnable {
class DeepRecursion implements Callable, Supplier, Runnable {
static enum Behaviour {
CALL, GET, RUN;
private static Behaviour[] values = values();
public static Behaviour choose(ThreadLocalRandom tlr) {
return values[tlr.nextInt(3)];
}
}
static enum Behaviour {CALL, RUN}
final Behaviour behaviour;
public DeepRecursion(Behaviour behaviour) {
@ -70,6 +76,8 @@ public class StressStackOverflow {
switch (behaviour) {
case CALL ->
ScopedValue.where(el, el.get() + 1).call(() -> fibonacci_pad(20, this));
case GET ->
ScopedValue.where(el, el.get() + 1).get(() -> fibonacci_pad(20, this));
case RUN ->
ScopedValue.where(el, el.get() + 1).run(() -> fibonacci_pad(20, this));
}
@ -96,10 +104,14 @@ public class StressStackOverflow {
Thread.yield();
}
public Object call() {
public Object get() {
run();
return null;
}
public Object call() {
return get();
}
}
static final Runnable nop = new Runnable() {
@ -142,12 +154,12 @@ public class StressStackOverflow {
var threadFactory
= (tlr.nextBoolean() ? Thread.ofPlatform() : Thread.ofVirtual()).factory();
try (var scope = new StructuredTaskScope<Object>("", threadFactory)) {
var future = scope.fork(() -> {
var handle = scope.fork(() -> {
op.run();
return null;
});
future.get();
scope.join();
handle.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -159,9 +171,9 @@ public class StressStackOverflow {
try (var scope = new StructuredTaskScope<Object>()) {
try {
if (tlr.nextBoolean()) {
// Repeatedly test Scoped Values set by ScopedValue::call() and ScopedValue::run()
// Repeatedly test Scoped Values set by ScopedValue::call(), get(), and run()
final var deepRecursion
= new DeepRecursion(tlr.nextBoolean() ? DeepRecursion.Behaviour.CALL : DeepRecursion.Behaviour.RUN);
= new DeepRecursion(DeepRecursion.Behaviour.choose(tlr));
deepRecursion.run();
} else {
// Recursively run ourself until we get a stack overflow

File diff suppressed because it is too large Load Diff

View File

@ -23,14 +23,14 @@
/*
* @test
* @bug 8284199
* @bug 8284199 8296779 8306647
* @summary Test thread dumps with StructuredTaskScope
* @modules jdk.incubator.concurrent
* @enablePreview
* @library /test/lib
* @run junit/othervm StructuredThreadDumpTest
*/
import jdk.incubator.concurrent.StructuredTaskScope;
import java.util.concurrent.StructuredTaskScope;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;

View File

@ -23,15 +23,15 @@
/*
* @test
* @bug 8284199 8296779 8306647
* @summary Basic tests for StructuredTaskScope with scoped values
* @modules jdk.incubator.concurrent
* @enablePreview
* @run junit WithScopedValue
*/
import jdk.incubator.concurrent.ScopedValue;
import jdk.incubator.concurrent.StructuredTaskScope;
import jdk.incubator.concurrent.StructureViolationException;
import java.util.concurrent.Future;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructuredTaskScope.Subtask;
import java.util.concurrent.StructureViolationException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
@ -54,13 +54,13 @@ class WithScopedValue {
@MethodSource("factories")
void testForkInheritsScopedValue1(ThreadFactory factory) throws Exception {
ScopedValue<String> name = ScopedValue.newInstance();
String value = ScopedValue.where(name, "x", () -> {
String value = ScopedValue.callWhere(name, "x", () -> {
try (var scope = new StructuredTaskScope<String>(null, factory)) {
Future<String> future = scope.fork(() -> {
Subtask<String> subtask = scope.fork(() -> {
return name.get(); // child should read "x"
});
scope.join();
return future.resultNow();
return subtask.get();
}
});
assertEquals(value, "x");
@ -73,19 +73,19 @@ class WithScopedValue {
@MethodSource("factories")
void testForkInheritsScopedValue2(ThreadFactory factory) throws Exception {
ScopedValue<String> name = ScopedValue.newInstance();
String value = ScopedValue.where(name, "x", () -> {
String value = ScopedValue.callWhere(name, "x", () -> {
try (var scope1 = new StructuredTaskScope<String>(null, factory)) {
Future<String> future1 = scope1.fork(() -> {
Subtask<String> subtask1 = scope1.fork(() -> {
try (var scope2 = new StructuredTaskScope<String>(null, factory)) {
Future<String> future2 = scope2.fork(() -> {
Subtask<String> subtask2 = scope2.fork(() -> {
return name.get(); // grandchild should read "x"
});
scope2.join();
return future2.resultNow();
return subtask2.get();
}
});
scope1.join();
return future1.resultNow();
return subtask1.get();
}
});
assertEquals(value, "x");
@ -98,19 +98,19 @@ class WithScopedValue {
@MethodSource("factories")
void testForkInheritsScopedValue3(ThreadFactory factory) throws Exception {
ScopedValue<String> name = ScopedValue.newInstance();
String value = ScopedValue.where(name, "x", () -> {
String value = ScopedValue.callWhere(name, "x", () -> {
try (var scope1 = new StructuredTaskScope<String>(null, factory)) {
Future<String> future1 = scope1.fork(() -> {
Subtask<String> subtask1 = scope1.fork(() -> {
assertEquals(name.get(), "x"); // child should read "x"
// rebind name to "y"
String grandchildValue = ScopedValue.where(name, "y", () -> {
String grandchildValue = ScopedValue.callWhere(name, "y", () -> {
try (var scope2 = new StructuredTaskScope<String>(null, factory)) {
Future<String> future2 = scope2.fork(() -> {
Subtask<String> subtask2 = scope2.fork(() -> {
return name.get(); // grandchild should read "y"
});
scope2.join();
return future2.resultNow();
return subtask2.get();
}
});
@ -118,7 +118,7 @@ class WithScopedValue {
return grandchildValue;
});
scope1.join();
return future1.resultNow();
return subtask1.get();
}
});
assertEquals(value, "y");
@ -136,23 +136,22 @@ class WithScopedValue {
var box = new Box();
try {
try {
ScopedValue.where(name, "x", () -> {
ScopedValue.runWhere(name, "x", () -> {
box.scope = new StructuredTaskScope<Object>();
});
fail();
} catch (StructureViolationException expected) { }
// underlying flock should be closed, fork should return a cancelled task
// underlying flock should be closed and fork should fail to start a thread
StructuredTaskScope<Object> scope = box.scope;
AtomicBoolean ran = new AtomicBoolean();
Future<Object> future = scope.fork(() -> {
Subtask<Object> subtask = scope.fork(() -> {
ran.set(true);
return null;
});
assertTrue(future.isCancelled());
scope.join();
assertEquals(Subtask.State.UNAVAILABLE, subtask.state());
assertFalse(ran.get());
} finally {
StructuredTaskScope<Object> scope = box.scope;
if (scope != null) {
@ -168,7 +167,7 @@ class WithScopedValue {
void testStructureViolation2() throws Exception {
ScopedValue<String> name = ScopedValue.newInstance();
try (var scope = new StructuredTaskScope<String>()) {
ScopedValue.where(name, "x", () -> {
ScopedValue.runWhere(name, "x", () -> {
assertThrows(StructureViolationException.class, scope::close);
});
}
@ -181,7 +180,7 @@ class WithScopedValue {
void testStructureViolation3() throws Exception {
ScopedValue<String> name = ScopedValue.newInstance();
try (var scope = new StructuredTaskScope<String>()) {
ScopedValue.where(name, "x", () -> {
ScopedValue.runWhere(name, "x", () -> {
assertThrows(StructureViolationException.class,
() -> scope.fork(() -> "foo"));
});
@ -197,9 +196,9 @@ class WithScopedValue {
ScopedValue<String> name2 = ScopedValue.newInstance();
// rebind
ScopedValue.where(name1, "x", () -> {
ScopedValue.runWhere(name1, "x", () -> {
try (var scope = new StructuredTaskScope<String>()) {
ScopedValue.where(name1, "y", () -> {
ScopedValue.runWhere(name1, "y", () -> {
assertThrows(StructureViolationException.class,
() -> scope.fork(() -> "foo"));
});
@ -207,9 +206,9 @@ class WithScopedValue {
});
// new binding
ScopedValue.where(name1, "x", () -> {
ScopedValue.runWhere(name1, "x", () -> {
try (var scope = new StructuredTaskScope<String>()) {
ScopedValue.where(name2, "y", () -> {
ScopedValue.runWhere(name2, "y", () -> {
assertThrows(StructureViolationException.class,
() -> scope.fork(() -> "foo"));
});

View File

@ -1042,7 +1042,7 @@ class ThreadFlockTest {
@Test
void testToString() {
try (var flock = ThreadFlock.open("xxxx")) {
assertTrue(flock.toString().contains("xxx"));
assertTrue(flock.toString().contains("xxxx"));
}
}

View File

@ -24,15 +24,14 @@
/*
* @test
* @summary Test ThreadFlock with scoped values
* @enablePreview
* @modules java.base/jdk.internal.misc
* @modules jdk.incubator.concurrent
* @run junit WithScopedValue
*/
import jdk.internal.misc.ThreadFlock;
import jdk.incubator.concurrent.ScopedValue;
import jdk.incubator.concurrent.StructureViolationException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.StructureViolationException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
@ -54,7 +53,7 @@ class WithScopedValue {
@MethodSource("factories")
void testInheritsScopedValue(ThreadFactory factory) throws Exception {
ScopedValue<String> name = ScopedValue.newInstance();
String value = ScopedValue.where(name, "duke", () -> {
String value = ScopedValue.callWhere(name, "duke", () -> {
var result = new AtomicReference<String>();
try (var flock = ThreadFlock.open(null)) {
Thread thread = factory.newThread(() -> {
@ -80,7 +79,7 @@ class WithScopedValue {
}
var box = new Box();
try {
ScopedValue.where(name, "x1", () -> {
ScopedValue.runWhere(name, "x1", () -> {
box.flock1 = ThreadFlock.open(null);
box.flock2 = ThreadFlock.open(null);
});
@ -98,11 +97,11 @@ class WithScopedValue {
void testStructureViolation2() {
ScopedValue<String> name = ScopedValue.newInstance();
try (var flock1 = ThreadFlock.open("flock1")) {
ScopedValue.where(name, "x1", () -> {
ScopedValue.runWhere(name, "x1", () -> {
try (var flock2 = ThreadFlock.open("flock2")) {
ScopedValue.where(name, "x2", () -> {
ScopedValue.runWhere(name, "x2", () -> {
try (var flock3 = ThreadFlock.open("flock3")) {
ScopedValue.where(name, "x3", () -> {
ScopedValue.runWhere(name, "x3", () -> {
var flock4 = ThreadFlock.open("flock4");
try {
@ -130,11 +129,11 @@ class WithScopedValue {
void testStructureViolation3() {
ScopedValue<String> name = ScopedValue.newInstance();
try (var flock1 = ThreadFlock.open("flock1")) {
ScopedValue.where(name, "x1", () -> {
ScopedValue.runWhere(name, "x1", () -> {
try (var flock2 = ThreadFlock.open("flock2")) {
ScopedValue.where(name, "x2", () -> {
ScopedValue.runWhere(name, "x2", () -> {
try (var flock3 = ThreadFlock.open("flock3")) {
ScopedValue.where(name, "x3", () -> {
ScopedValue.runWhere(name, "x3", () -> {
var flock4 = ThreadFlock.open("flock4");
try {
@ -162,11 +161,11 @@ class WithScopedValue {
void testStructureViolation4() {
ScopedValue<String> name = ScopedValue.newInstance();
try (var flock1 = ThreadFlock.open("flock1")) {
ScopedValue.where(name, "x1", () -> {
ScopedValue.runWhere(name, "x1", () -> {
try (var flock2 = ThreadFlock.open("flock2")) {
ScopedValue.where(name, "x2", () -> {
ScopedValue.runWhere(name, "x2", () -> {
try (var flock3 = ThreadFlock.open("flock3")) {
ScopedValue.where(name, "x3", () -> {
ScopedValue.runWhere(name, "x3", () -> {
var flock4 = ThreadFlock.open("flock4");
try {
@ -194,7 +193,7 @@ class WithScopedValue {
void testStructureViolation5(ThreadFactory factory) throws Exception {
ScopedValue<String> name = ScopedValue.newInstance();
try (var flock = ThreadFlock.open(null)) {
ScopedValue.where(name, "duke", () -> {
ScopedValue.runWhere(name, "duke", () -> {
Thread thread = factory.newThread(() -> { });
assertThrows(StructureViolationException.class, () -> flock.start(thread));
});
@ -208,9 +207,9 @@ class WithScopedValue {
@MethodSource("factories")
void testStructureViolation6(ThreadFactory factory) throws Exception {
ScopedValue<String> name = ScopedValue.newInstance();
ScopedValue.where(name, "duke", () -> {
ScopedValue.runWhere(name, "duke", () -> {
try (var flock = ThreadFlock.open(null)) {
ScopedValue.where(name, "duchess", () -> {
ScopedValue.runWhere(name, "duchess", () -> {
Thread thread = factory.newThread(() -> { });
assertThrows(StructureViolationException.class, () -> flock.start(thread));
});

View File

@ -22,14 +22,15 @@
*/
package org.openjdk.bench.jdk.incubator.concurrent;
package org.openjdk.bench.java.lang;
import jdk.incubator.concurrent.ScopedValue;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import static org.openjdk.bench.jdk.incubator.concurrent.ScopedValuesData.*;
import static org.openjdk.bench.java.lang.ScopedValuesData.*;
/**
* Tests ScopedValue
@ -40,10 +41,9 @@ import static org.openjdk.bench.jdk.incubator.concurrent.ScopedValuesData.*;
@Measurement(iterations=10, time=1)
@Threads(1)
@Fork(value = 1,
jvmArgsPrepend = {"-Djmh.executor.class=org.openjdk.bench.jdk.incubator.concurrent.ScopedValuesExecutorService",
jvmArgsPrepend = {"-Djmh.executor.class=org.openjdk.bench.java.lang.ScopedValuesExecutorService",
"-Djmh.executor=CUSTOM",
"-Djmh.blackhole.mode=COMPILER",
"--add-modules=jdk.incubator.concurrent",
"--enable-preview"})
@State(Scope.Thread)
@SuppressWarnings("preview")
@ -161,12 +161,21 @@ public class ScopedValues {
}
// Test 4: The cost of binding, but not using any result
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public Object bind_ScopedValue() throws Exception {
return HOLD_42.call(this::getClass);
return HOLD_42.call(aCallable);
}
private static final Callable<Class<?>> aCallable = () -> ScopedValues.class;
// Same, but make sure that Carrier.get(Supplier) is no slower
// than Carrier.call(Callable).
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public Object bindViaGet_ScopedValue() {
return HOLD_42.get(aSupplier);
}
private static final Supplier<Class<?>> aSupplier = () -> ScopedValues.class;
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)

View File

@ -21,9 +21,8 @@
* questions.
*/
package org.openjdk.bench.jdk.incubator.concurrent;
package org.openjdk.bench.java.lang;
import jdk.incubator.concurrent.ScopedValue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2023, 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
@ -21,8 +21,7 @@
* questions.
*/
package org.openjdk.bench.jdk.incubator.concurrent;
package org.openjdk.bench.java.lang;
import java.util.concurrent.*;