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.hotspot.agent \
jdk.httpserver \ jdk.httpserver \
jdk.jpackage \ jdk.jpackage \
jdk.incubator.concurrent \
jdk.incubator.vector \ jdk.incubator.vector \
jdk.jartool \ jdk.jartool \
jdk.javadoc \ jdk.javadoc \

View File

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

View File

@ -118,6 +118,8 @@
template(java_lang_StringBuilder, "java/lang/StringBuilder") \ template(java_lang_StringBuilder, "java/lang/StringBuilder") \
template(java_lang_CharSequence, "java/lang/CharSequence") \ template(java_lang_CharSequence, "java/lang/CharSequence") \
template(java_lang_SecurityManager, "java/lang/SecurityManager") \ 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_AccessControlContext, "java/security/AccessControlContext") \
template(java_security_AccessController, "java/security/AccessController") \ template(java_security_AccessController, "java/security/AccessController") \
template(executePrivileged_name, "executePrivileged") \ template(executePrivileged_name, "executePrivileged") \
@ -157,8 +159,6 @@
template(jdk_internal_loader_BuiltinClassLoader, "jdk/internal/loader/BuiltinClassLoader") \ 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_AppClassLoader, "jdk/internal/loader/ClassLoaders$AppClassLoader") \
template(jdk_internal_loader_ClassLoaders_PlatformClassLoader, "jdk/internal/loader/ClassLoaders$PlatformClassLoader") \ 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 */ \ /* Java runtime version access */ \
template(java_lang_VersionProps, "java/lang/VersionProps") \ template(java_lang_VersionProps, "java/lang/VersionProps") \

View File

@ -1362,7 +1362,7 @@ class ScopedValueBindingsResolver {
public: public:
InstanceKlass* Carrier_klass; InstanceKlass* Carrier_klass;
ScopedValueBindingsResolver(JavaThread* THREAD) { 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); Carrier_klass = InstanceKlass::cast(k);
} }
}; };
@ -1395,7 +1395,7 @@ JVM_ENTRY(jobject, JVM_FindScopedValueBindings(JNIEnv *env, jclass cls))
if (loc != -1) { if (loc != -1) {
javaVFrame *frame = vfst.asJavaVFrame(); javaVFrame *frame = vfst.asJavaVFrame();
StackValueCollection* locals = frame->locals(); 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(); Handle result = head_sv->get_obj();
assert(!head_sv->obj_is_scalar_replaced(), "found scalar-replaced object"); assert(!head_sv->obj_is_scalar_replaced(), "found scalar-replaced object");
if (result() != nullptr) { 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. * Copyright (c) 2020, 2022, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
@ -24,109 +24,141 @@
* questions. * questions.
*/ */
package jdk.incubator.concurrent; package java.lang;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Objects; import java.util.Objects;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructureViolationException;
import java.util.function.Supplier; import java.util.function.Supplier;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.JavaUtilConcurrentTLRAccess; import jdk.internal.access.JavaUtilConcurrentTLRAccess;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden; import jdk.internal.vm.annotation.Hidden;
import jdk.internal.vm.annotation.Stable;
import jdk.internal.vm.ScopedValueContainer; import jdk.internal.vm.ScopedValueContainer;
import sun.security.action.GetPropertyAction; import sun.security.action.GetPropertyAction;
/** /**
* A value that is set once and is then available for reading for a bounded period of * A value that may be safely and efficiently shared to methods without using method
* execution by a thread. A {@code ScopedValue} allows for safely and efficiently sharing * parameters.
* data for a bounded period of execution without passing the data as method arguments.
* *
* <p> {@code ScopedValue} defines the {@link #where(ScopedValue, Object, Runnable)} * <p> In the Java programming language, data is usually passed to a method by means of a
* method to set the value of a {@code ScopedValue} for the bouned period of execution by * method parameter. The data may need to be passed through a sequence of many methods to
* a thread of the runnable's {@link Runnable#run() run} method. The unfolding execution of * get to the method that makes use of the data. Every method in the sequence of calls
* the methods executed by {@code run} defines a <b><em>dynamic scope</em></b>. The scoped * needs to declare the parameter and every method has access to the data.
* value is {@linkplain #isBound() bound} while executing in the dynamic scope, it reverts * {@code ScopedValue} provides a means to pass data to a faraway method (typically a
* to being <em>unbound</em> when the {@code run} method completes (normally or with an * <em>callback</em>) without using method parameters. In effect, a {@code ScopedValue}
* exception). Code executing in the dynamic scope uses the {@code ScopedValue} {@link * is an <em>implicit method parameter</em>. It is "as if" every method in a sequence of
* #get() get} method to read its value. * 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 * <p> The {@code ScopedValue} API works by executing a method with a {@code ScopedValue}
* incarnations, one per thread. The particular incarnation that is used depends on which * object <em>bound</em> to some value for the bounded period of execution of a method.
* thread calls its methods. * 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 * <p> Consider the following example with a scoped value "{@code NAME}" bound to the value
* <em>bound</em> to the value "{@code duke}" for the execution, by a thread, of a run * "{@code duke}" for the execution of a {@code run} method. The {@code run} method, in
* method that invokes {@code doSomething()}. * turn, invokes {@code doSomething}.
* {@snippet lang=java : * {@snippet lang=java :
* // @link substring="newInstance" target="#newInstance" : * // @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 * Code executed directly or indirectly by {@code doSomething}, with access to the field
* USERNAME.get()} will read the value "{@code duke}". The scoped value is bound while * {@code NAME}, can invoke {@code NAME.get()} to read the value "{@code duke}". {@code
* executing {@code doSomething()} and becomes unbound when {@code doSomething()} * NAME} is bound while executing the {@code run} method. It reverts to being unbound when
* completes (normally or with an exception). If one thread were to call {@code * the {@code run} method completes.
* 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.
* *
* <p> In addition to the {@code where} method that executes a {@code run} method, {@code * <p> The example using {@code runWhere} invokes a method that does not return a result.
* ScopedValue} defines the {@link #where(ScopedValue, Object, Callable)} method to execute * The {@link #callWhere(ScopedValue, Object, Callable) callWhere} and {@link
* a method that returns a result. It also defines the {@link #where(ScopedValue, Object)} * #getWhere(ScopedValue, Object, Supplier) getWhere} can be used to invoke a method that
* method for cases where it is useful to accumulate mappings of {@code ScopedValue} to * returns a result.
* value. * 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 * <h2>Bindings are per-thread</h2>
* static} field. The accessibility of the field will determine which components can
* bind or read its value.
* *
* <p> Unless otherwise specified, passing a {@code null} argument to a method in this * A {@code ScopedValue} binding to a value is per-thread. Invoking {@code xxxWhere}
* class will cause a {@link NullPointerException} to be thrown. * 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> * <h2><a id="rebind">Rebinding</a></h2>
* *
* The {@code ScopedValue} API allows a new binding to be established for <em>nested * 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 * 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 * 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 * dynamic scope. When the method completes, the value of the {@code ScopedValue} reverts
* the {@code ScopedValue} reverts to its previous value. * to its previous value.
* *
* <p> In the above example, suppose that code executed by {@code doSomething()} binds * <p> In the above example, suppose that code executed by {@code doSomething} binds
* {@code USERNAME} to a new value with: * {@code NAME} to a new value with:
* {@snippet lang=java : * {@snippet lang=java :
* ScopedValue.where(USERNAME, "duchess", () -> doMore()); * ScopedValue.runWhere(NAME, "duchess", () -> doMore());
* } * }
* Code executed directly or indirectly by {@code doMore()} that invokes {@code * Code executed directly or indirectly by {@code doMore()} that invokes {@code
* USERNAME.get()} will read the value "{@code duchess}". When {@code doMore()} completes * NAME.get()} will read the value "{@code duchess}". When {@code doMore()} completes
* (normally or with an exception), the value of {@code USERNAME} reverts to * then the value of {@code NAME} reverts to "{@code duke}".
* "{@code duke}".
* *
* <h2><a id="inheritance">Inheritance</a></h2> * <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 * 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 * period of execution by a parent thread. When using a {@link StructuredTaskScope},
* StructuredTaskScope}, scoped value bindings are <em>captured</em> when creating a * scoped value bindings are <em>captured</em> when creating a {@code StructuredTaskScope}
* {@code StructuredTaskScope} and inherited by all threads started in that scope with * and inherited by all threads started in that task scope with the
* the {@link StructuredTaskScope#fork(Callable) fork} method. * {@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 * 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 * run} method creates a {@code StructuredTaskScope} that forks three tasks. Code executed
* executed directly or indirectly by these threads running {@code childTask1()}, * directly or indirectly by these threads running {@code childTask1()}, {@code childTask2()},
* {@code childTask2()}, and {@code childTask3()} will read the value "{@code duke}". * and {@code childTask3()} that invokes {@code NAME.get()} will read the value
* "{@code duke}".
* *
* {@snippet lang=java : * {@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>()) { * try (var scope = new StructuredTaskScope<String>()) {
* *
* scope.fork(() -> childTask1()); * 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 * @implNote
* Scoped values are designed to be used in fairly small * Scoped values are designed to be used in fairly small
* numbers. {@link #get} initially performs a search through enclosing * 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 * 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. * 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 * provides some system properties to tune the performance of scoped
* values. * 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 * 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, * for the performance of scoped values. If it is too small,
* the runtime library will repeatedly need to scan for each * 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} * be varied from 2 to 16 entries in size. {@code ScopedValue.cacheSize}
* must be an integer power of 2. * 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}. * <p>The other system property is {@code jdk.preserveScopedValueCache}.
* This property determines whether the per-thread scoped-value * 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 * memory saving, but each virtual thread's scoped-value cache would
* have to be regenerated after a blocking operation. * have to be regenerated after a blocking operation.
* *
* @param <T> the type of the object bound to this {@code ScopedValue} * @param <T> the type of the value
* @since 20 * @since 21
*/ */
@PreviewFeature(feature = PreviewFeature.Feature.SCOPED_VALUES)
public final class ScopedValue<T> { public final class ScopedValue<T> {
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private final int hash;
private final @Stable int hash;
@Override @Override
public int hashCode() { return hash; } 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. * 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 * {@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} * 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}. * 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 * <p> Unless otherwise specified, passing a {@code null} argument to a method in
* this class will cause a {@link NullPointerException} to be thrown. * 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 { public static final class Carrier {
// Bit masks: a 1 in postion n indicates that this set of bound values // Bit masks: a 1 in postion n indicates that this set of bound values
// hits that slot in the cache. // 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. * Add a binding to this map, returning a new Carrier instance.
*/ */
private static final <T> Carrier where(ScopedValue<T> key, T value, private static <T> Carrier where(ScopedValue<T> key, T value, Carrier prev) {
Carrier prev) {
return new Carrier(key, value, prev); return new Carrier(key, value, prev);
} }
@ -311,11 +359,11 @@ public final class ScopedValue<T> {
return where(key, value, null); return where(key, value, null);
} }
final Object get() { Object get() {
return value; return value;
} }
final ScopedValue<?> getKey() { ScopedValue<?> getKey() {
return key; return key;
} }
@ -360,7 +408,7 @@ public final class ScopedValue<T> {
* @param <R> the type of the result of the operation * @param <R> the type of the result of the operation
* @return the result * @return the result
* @throws Exception if {@code op} completes with an exception * @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 { public <R> R call(Callable<? extends R> op) throws Exception {
Objects.requireNonNull(op); Objects.requireNonNull(op);
@ -370,6 +418,48 @@ public final class ScopedValue<T> {
return runWith(newSnapshot, op); 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. * Execute the action with a set of ScopedValue bindings.
* *
@ -379,14 +469,14 @@ public final class ScopedValue<T> {
*/ */
@Hidden @Hidden
@ForceInline @ForceInline
private <R> R runWith(Snapshot newSnapshot, Callable<R> op) throws Exception { private <R> R runWith(Snapshot newSnapshot, Callable<R> op) {
try { try {
JLA.setScopedValueBindings(newSnapshot); Thread.setScopedValueBindings(newSnapshot);
JLA.ensureMaterializedForStackWalk(newSnapshot); Thread.ensureMaterializedForStackWalk(newSnapshot);
return ScopedValueContainer.call(op); return ScopedValueContainer.call(op);
} finally { } finally {
Reference.reachabilityFence(newSnapshot); Reference.reachabilityFence(newSnapshot);
JLA.setScopedValueBindings(newSnapshot.prev); Thread.setScopedValueBindings(newSnapshot.prev);
Cache.invalidate(bitmask); Cache.invalidate(bitmask);
} }
} }
@ -407,7 +497,7 @@ public final class ScopedValue<T> {
* they were created. Once closed, {@link StructureViolationException} is thrown. * they were created. Once closed, {@link StructureViolationException} is thrown.
* *
* @param op the operation to run * @param op the operation to run
* @see ScopedValue#where(ScopedValue, Object, Runnable) * @see ScopedValue#runWhere(ScopedValue, Object, Runnable)
*/ */
public void run(Runnable op) { public void run(Runnable op) {
Objects.requireNonNull(op); Objects.requireNonNull(op);
@ -428,12 +518,12 @@ public final class ScopedValue<T> {
@ForceInline @ForceInline
private void runWith(Snapshot newSnapshot, Runnable op) { private void runWith(Snapshot newSnapshot, Runnable op) {
try { try {
JLA.setScopedValueBindings(newSnapshot); Thread.setScopedValueBindings(newSnapshot);
JLA.ensureMaterializedForStackWalk(newSnapshot); Thread.ensureMaterializedForStackWalk(newSnapshot);
ScopedValueContainer.run(op); ScopedValueContainer.run(op);
} finally { } finally {
Reference.reachabilityFence(newSnapshot); Reference.reachabilityFence(newSnapshot);
JLA.setScopedValueBindings(newSnapshot.prev); Thread.setScopedValueBindings(newSnapshot.prev);
Cache.invalidate(bitmask); Cache.invalidate(bitmask);
} }
} }
@ -488,12 +578,46 @@ public final class ScopedValue<T> {
* @return the result * @return the result
* @throws Exception if the operation completes with an exception * @throws Exception if the operation completes with an exception
*/ */
public static <T, R> R where(ScopedValue<T> key, public static <T, R> R callWhere(ScopedValue<T> key,
T value, T value,
Callable<? extends R> op) throws Exception { Callable<? extends R> op) throws Exception {
return where(key, value).call(op); 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 * 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 * 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 <T> the type of the value
* @param op the operation to call * @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); where(key, value).run(op);
} }
@ -642,11 +766,11 @@ public final class ScopedValue<T> {
} }
private static Object[] scopedValueCache() { private static Object[] scopedValueCache() {
return JLA.scopedValueCache(); return Thread.scopedValueCache();
} }
private static void setScopedValueCache(Object[] cache) { private static void setScopedValueCache(Object[] cache) {
JLA.setScopedValueCache(cache); Thread.setScopedValueCache(cache);
} }
// Special value to indicate this is a newly-created Thread // 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 // 3: A Snapshot instance: this contains one or more scoped value
// bindings. // bindings.
// 4: null: there may be some bindings in this Thread, but we don't know // 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. // the stack to find them.
Object bindings = JLA.scopedValueBindings(); Object bindings = Thread.scopedValueBindings();
if (bindings == NEW_THREAD_BINDINGS) { if (bindings == NEW_THREAD_BINDINGS) {
// This must be a new thread // This must be a new thread
return Snapshot.EMPTY_SNAPSHOT; return Snapshot.EMPTY_SNAPSHOT;
} }
if (bindings == null) { if (bindings == null) {
// Search the stack // Search the stack
bindings = JLA.findScopedValueBindings(); bindings = Thread.findScopedValueBindings();
if (bindings == null) { if (bindings == null) {
// Nothing on the stack. // Nothing on the stack.
bindings = Snapshot.EMPTY_SNAPSHOT; bindings = Snapshot.EMPTY_SNAPSHOT;
} }
} }
assert (bindings != null); assert (bindings != null);
JLA.setScopedValueBindings(bindings); Thread.setScopedValueBindings(bindings);
return (Snapshot) bindings; return (Snapshot) bindings;
} }
@ -732,7 +856,7 @@ public final class ScopedValue<T> {
private static final int MAX_CACHE_SIZE = 16; private static final int MAX_CACHE_SIZE = 16;
static { static {
final String propertyName = "jdk.incubator.concurrent.ScopedValue.cacheSize"; final String propertyName = "java.lang.ScopedValue.cacheSize";
var sizeString = GetPropertyAction.privilegedGetProperty(propertyName, "16"); var sizeString = GetPropertyAction.privilegedGetProperty(propertyName, "16");
var cacheSize = Integer.valueOf(sizeString); var cacheSize = Integer.valueOf(sizeString);
if (cacheSize < 2 || cacheSize > MAX_CACHE_SIZE) { if (cacheSize < 2 || cacheSize > MAX_CACHE_SIZE) {

View File

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

View File

@ -37,9 +37,9 @@ import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.StructureViolationException;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;
import jdk.internal.event.ThreadSleepEvent; import jdk.internal.event.ThreadSleepEvent;
import jdk.internal.misc.StructureViolationExceptions;
import jdk.internal.misc.TerminatingThreadLocal; import jdk.internal.misc.TerminatingThreadLocal;
import jdk.internal.misc.Unsafe; import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM; import jdk.internal.misc.VM;
@ -321,7 +321,7 @@ public class Thread implements Runnable {
// bindings established for running/calling an operation // bindings established for running/calling an operation
Object bindings = snapshot.scopedValueBindings(); Object bindings = snapshot.scopedValueBindings();
if (currentThread().scopedValueBindings != bindings) { if (currentThread().scopedValueBindings != bindings) {
StructureViolationExceptions.throwException("Scoped value bindings have changed"); throw new StructureViolationException("Scoped value bindings have changed");
} }
this.scopedValueBindings = bindings; 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package jdk.incubator.concurrent; package java.util.concurrent;
import jdk.internal.javac.PreviewFeature;
/** /**
* Thrown when a structure violation is detected. * Thrown when a structure violation is detected.
* *
* @see StructuredTaskScope#close() * @see StructuredTaskScope#close()
* *
* @since 19 * @since 21
*/ */
@PreviewFeature(feature = PreviewFeature.Feature.STRUCTURED_CONCURRENCY)
public final class StructureViolationException extends RuntimeException { public final class StructureViolationException extends RuntimeException {
@java.io.Serial @java.io.Serial
private static final long serialVersionUID = -7705327650798235468L; 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(); MethodHandles.Lookup l = MethodHandles.lookup();
STATE = l.findVarHandle(ThreadPerTaskExecutor.class, "state", int.class); STATE = l.findVarHandle(ThreadPerTaskExecutor.class, "state", int.class);
} catch (Exception e) { } catch (Exception e) {
throw new InternalError(e); throw new ExceptionInInitializerError(e);
} }
} }

View File

@ -531,15 +531,6 @@ public interface JavaLangAccess {
*/ */
Object scopedValueBindings(); 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 * Returns the innermost mounted continuation
*/ */

View File

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

View File

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

View File

@ -26,7 +26,6 @@ package jdk.internal.vm;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -95,6 +94,11 @@ public class SharedThreadContainer extends ThreadContainer implements AutoClosea
return create(ThreadContainers.root(), name); return create(ThreadContainers.root(), name);
} }
@Override
public String name() {
return name;
}
@Override @Override
public Thread owner() { public Thread owner() {
return null; return null;
@ -159,14 +163,4 @@ public class SharedThreadContainer extends ThreadContainer implements AutoClosea
ThreadContainers.deregisterContainer(key); 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,6 +25,7 @@
package jdk.internal.vm; package jdk.internal.vm;
import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@ -41,6 +42,13 @@ public abstract class ThreadContainer extends StackableScope {
super(shared); 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. * 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() { public ScopedValueContainer.BindingsSnapshot scopedValueBindings() {
return null; 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; return null;
} }
@Override @Override
public String toString() { public String name() {
return "<root>"; return "<root>";
} }
@Override @Override
public StackableScope previous() { public StackableScope previous() {
return null; return null;
} }
@Override
public String toString() {
return name();
}
/** /**
* Returns the platform threads that are not in the container as these * Returns the platform threads that are not in the container as these

View File

@ -162,7 +162,6 @@ module java.base {
jdk.jlink, jdk.jlink,
jdk.jfr, jdk.jfr,
jdk.net, jdk.net,
jdk.incubator.concurrent,
jdk.sctp, jdk.sctp,
jdk.crypto.cryptoki; jdk.crypto.cryptoki;
exports jdk.internal.foreign to exports jdk.internal.foreign to
@ -222,7 +221,6 @@ module java.base {
jdk.charsets, jdk.charsets,
jdk.compiler, jdk.compiler,
jdk.crypto.cryptoki, jdk.crypto.cryptoki,
jdk.incubator.concurrent,
jdk.incubator.vector, jdk.incubator.vector,
jdk.jfr, jdk.jfr,
jdk.jshell, jdk.jshell,
@ -256,7 +254,6 @@ module java.base {
jdk.unsupported; jdk.unsupported;
exports jdk.internal.vm to exports jdk.internal.vm to
java.management, java.management,
jdk.incubator.concurrent,
jdk.internal.jvmstat, jdk.internal.jvmstat,
jdk.management, jdk.management,
jdk.management.agent, jdk.management.agent,
@ -264,7 +261,6 @@ module java.base {
exports jdk.internal.vm.annotation to exports jdk.internal.vm.annotation to
java.instrument, java.instrument,
jdk.internal.vm.ci, jdk.internal.vm.ci,
jdk.incubator.concurrent,
jdk.incubator.vector, jdk.incubator.vector,
jdk.jfr, jdk.jfr,
jdk.unsupported; jdk.unsupported;
@ -326,8 +322,7 @@ module java.base {
exports sun.security.action to exports sun.security.action to
java.desktop, java.desktop,
java.security.jgss, java.security.jgss,
jdk.crypto.ec, jdk.crypto.ec;
jdk.incubator.concurrent;
exports sun.security.internal.interfaces to exports sun.security.internal.interfaces to
jdk.crypto.cryptoki; jdk.crypto.cryptoki;
exports sun.security.internal.spec to 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/lambda/LambdaFileEncodingSerialization.java 8249079 linux-x64
java/lang/invoke/RicochetTest.java 8251969 generic-all java/lang/invoke/RicochetTest.java 8251969 generic-all
java/lang/ScopedValue/StressStackOverflow.java 8303498 linux-s390x
############################################################################ ############################################################################
# jdk_instrument # jdk_instrument
@ -739,9 +741,6 @@ sun/tools/jhsdb/JStackStressTest.java 8276210 linux-aa
javax/rmi/ssl/SSLSocketParametersTest.sh 8162906 generic-all 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/ShortMaxVectorTests.java 8306592 generic-i586
jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows-x64 jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows-x64

View File

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

View File

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

View File

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

View File

@ -24,8 +24,7 @@
/** /**
* @test * @test
* @summary StressStackOverflow the recovery path for ScopedValue * @summary StressStackOverflow the recovery path for ScopedValue
* @modules jdk.incubator.concurrent * @enablePreview
* @compile StressStackOverflow.java
* @run main/othervm/timeout=300 -XX:-TieredCompilation StressStackOverflow * @run main/othervm/timeout=300 -XX:-TieredCompilation StressStackOverflow
* @run main/othervm/timeout=300 -XX:TieredStopAtLevel=1 StressStackOverflow * @run main/othervm/timeout=300 -XX:TieredStopAtLevel=1 StressStackOverflow
* @run main/othervm/timeout=300 StressStackOverflow * @run main/othervm/timeout=300 StressStackOverflow
@ -34,9 +33,9 @@
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import jdk.incubator.concurrent.ScopedValue; import java.util.concurrent.StructureViolationException;
import jdk.incubator.concurrent.StructureViolationException; import java.util.concurrent.StructuredTaskScope;
import jdk.incubator.concurrent.StructuredTaskScope; import java.util.function.Supplier;
public class StressStackOverflow { public class StressStackOverflow {
public static final ScopedValue<Integer> el = ScopedValue.newInstance(); 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 // Test the ScopedValue recovery mechanism for stack overflows. We implement both Callable
// and Runnable interfaces. Which one gets tested depends on the constructor argument. // 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; final Behaviour behaviour;
public DeepRecursion(Behaviour behaviour) { public DeepRecursion(Behaviour behaviour) {
@ -70,6 +76,8 @@ public class StressStackOverflow {
switch (behaviour) { switch (behaviour) {
case CALL -> case CALL ->
ScopedValue.where(el, el.get() + 1).call(() -> fibonacci_pad(20, this)); 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 -> case RUN ->
ScopedValue.where(el, el.get() + 1).run(() -> fibonacci_pad(20, this)); ScopedValue.where(el, el.get() + 1).run(() -> fibonacci_pad(20, this));
} }
@ -96,10 +104,14 @@ public class StressStackOverflow {
Thread.yield(); Thread.yield();
} }
public Object call() { public Object get() {
run(); run();
return null; return null;
} }
public Object call() {
return get();
}
} }
static final Runnable nop = new Runnable() { static final Runnable nop = new Runnable() {
@ -142,12 +154,12 @@ public class StressStackOverflow {
var threadFactory var threadFactory
= (tlr.nextBoolean() ? Thread.ofPlatform() : Thread.ofVirtual()).factory(); = (tlr.nextBoolean() ? Thread.ofPlatform() : Thread.ofVirtual()).factory();
try (var scope = new StructuredTaskScope<Object>("", threadFactory)) { try (var scope = new StructuredTaskScope<Object>("", threadFactory)) {
var future = scope.fork(() -> { var handle = scope.fork(() -> {
op.run(); op.run();
return null; return null;
}); });
future.get();
scope.join(); scope.join();
handle.get();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -159,9 +171,9 @@ public class StressStackOverflow {
try (var scope = new StructuredTaskScope<Object>()) { try (var scope = new StructuredTaskScope<Object>()) {
try { try {
if (tlr.nextBoolean()) { 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 final var deepRecursion
= new DeepRecursion(tlr.nextBoolean() ? DeepRecursion.Behaviour.CALL : DeepRecursion.Behaviour.RUN); = new DeepRecursion(DeepRecursion.Behaviour.choose(tlr));
deepRecursion.run(); deepRecursion.run();
} else { } else {
// Recursively run ourself until we get a stack overflow // 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 * @test
* @bug 8284199 * @bug 8284199 8296779 8306647
* @summary Test thread dumps with StructuredTaskScope * @summary Test thread dumps with StructuredTaskScope
* @modules jdk.incubator.concurrent * @enablePreview
* @library /test/lib * @library /test/lib
* @run junit/othervm StructuredThreadDumpTest * @run junit/othervm StructuredThreadDumpTest
*/ */
import jdk.incubator.concurrent.StructuredTaskScope; import java.util.concurrent.StructuredTaskScope;
import java.io.IOException; import java.io.IOException;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.nio.file.Files; import java.nio.file.Files;

View File

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

View File

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

View File

@ -24,15 +24,14 @@
/* /*
* @test * @test
* @summary Test ThreadFlock with scoped values * @summary Test ThreadFlock with scoped values
* @enablePreview
* @modules java.base/jdk.internal.misc * @modules java.base/jdk.internal.misc
* @modules jdk.incubator.concurrent
* @run junit WithScopedValue * @run junit WithScopedValue
*/ */
import jdk.internal.misc.ThreadFlock; import jdk.internal.misc.ThreadFlock;
import jdk.incubator.concurrent.ScopedValue;
import jdk.incubator.concurrent.StructureViolationException;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.StructureViolationException;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -54,7 +53,7 @@ class WithScopedValue {
@MethodSource("factories") @MethodSource("factories")
void testInheritsScopedValue(ThreadFactory factory) throws Exception { void testInheritsScopedValue(ThreadFactory factory) throws Exception {
ScopedValue<String> name = ScopedValue.newInstance(); ScopedValue<String> name = ScopedValue.newInstance();
String value = ScopedValue.where(name, "duke", () -> { String value = ScopedValue.callWhere(name, "duke", () -> {
var result = new AtomicReference<String>(); var result = new AtomicReference<String>();
try (var flock = ThreadFlock.open(null)) { try (var flock = ThreadFlock.open(null)) {
Thread thread = factory.newThread(() -> { Thread thread = factory.newThread(() -> {
@ -80,7 +79,7 @@ class WithScopedValue {
} }
var box = new Box(); var box = new Box();
try { try {
ScopedValue.where(name, "x1", () -> { ScopedValue.runWhere(name, "x1", () -> {
box.flock1 = ThreadFlock.open(null); box.flock1 = ThreadFlock.open(null);
box.flock2 = ThreadFlock.open(null); box.flock2 = ThreadFlock.open(null);
}); });
@ -98,11 +97,11 @@ class WithScopedValue {
void testStructureViolation2() { void testStructureViolation2() {
ScopedValue<String> name = ScopedValue.newInstance(); ScopedValue<String> name = ScopedValue.newInstance();
try (var flock1 = ThreadFlock.open("flock1")) { try (var flock1 = ThreadFlock.open("flock1")) {
ScopedValue.where(name, "x1", () -> { ScopedValue.runWhere(name, "x1", () -> {
try (var flock2 = ThreadFlock.open("flock2")) { try (var flock2 = ThreadFlock.open("flock2")) {
ScopedValue.where(name, "x2", () -> { ScopedValue.runWhere(name, "x2", () -> {
try (var flock3 = ThreadFlock.open("flock3")) { try (var flock3 = ThreadFlock.open("flock3")) {
ScopedValue.where(name, "x3", () -> { ScopedValue.runWhere(name, "x3", () -> {
var flock4 = ThreadFlock.open("flock4"); var flock4 = ThreadFlock.open("flock4");
try { try {
@ -130,11 +129,11 @@ class WithScopedValue {
void testStructureViolation3() { void testStructureViolation3() {
ScopedValue<String> name = ScopedValue.newInstance(); ScopedValue<String> name = ScopedValue.newInstance();
try (var flock1 = ThreadFlock.open("flock1")) { try (var flock1 = ThreadFlock.open("flock1")) {
ScopedValue.where(name, "x1", () -> { ScopedValue.runWhere(name, "x1", () -> {
try (var flock2 = ThreadFlock.open("flock2")) { try (var flock2 = ThreadFlock.open("flock2")) {
ScopedValue.where(name, "x2", () -> { ScopedValue.runWhere(name, "x2", () -> {
try (var flock3 = ThreadFlock.open("flock3")) { try (var flock3 = ThreadFlock.open("flock3")) {
ScopedValue.where(name, "x3", () -> { ScopedValue.runWhere(name, "x3", () -> {
var flock4 = ThreadFlock.open("flock4"); var flock4 = ThreadFlock.open("flock4");
try { try {
@ -162,11 +161,11 @@ class WithScopedValue {
void testStructureViolation4() { void testStructureViolation4() {
ScopedValue<String> name = ScopedValue.newInstance(); ScopedValue<String> name = ScopedValue.newInstance();
try (var flock1 = ThreadFlock.open("flock1")) { try (var flock1 = ThreadFlock.open("flock1")) {
ScopedValue.where(name, "x1", () -> { ScopedValue.runWhere(name, "x1", () -> {
try (var flock2 = ThreadFlock.open("flock2")) { try (var flock2 = ThreadFlock.open("flock2")) {
ScopedValue.where(name, "x2", () -> { ScopedValue.runWhere(name, "x2", () -> {
try (var flock3 = ThreadFlock.open("flock3")) { try (var flock3 = ThreadFlock.open("flock3")) {
ScopedValue.where(name, "x3", () -> { ScopedValue.runWhere(name, "x3", () -> {
var flock4 = ThreadFlock.open("flock4"); var flock4 = ThreadFlock.open("flock4");
try { try {
@ -194,7 +193,7 @@ class WithScopedValue {
void testStructureViolation5(ThreadFactory factory) throws Exception { void testStructureViolation5(ThreadFactory factory) throws Exception {
ScopedValue<String> name = ScopedValue.newInstance(); ScopedValue<String> name = ScopedValue.newInstance();
try (var flock = ThreadFlock.open(null)) { try (var flock = ThreadFlock.open(null)) {
ScopedValue.where(name, "duke", () -> { ScopedValue.runWhere(name, "duke", () -> {
Thread thread = factory.newThread(() -> { }); Thread thread = factory.newThread(() -> { });
assertThrows(StructureViolationException.class, () -> flock.start(thread)); assertThrows(StructureViolationException.class, () -> flock.start(thread));
}); });
@ -208,9 +207,9 @@ class WithScopedValue {
@MethodSource("factories") @MethodSource("factories")
void testStructureViolation6(ThreadFactory factory) throws Exception { void testStructureViolation6(ThreadFactory factory) throws Exception {
ScopedValue<String> name = ScopedValue.newInstance(); ScopedValue<String> name = ScopedValue.newInstance();
ScopedValue.where(name, "duke", () -> { ScopedValue.runWhere(name, "duke", () -> {
try (var flock = ThreadFlock.open(null)) { try (var flock = ThreadFlock.open(null)) {
ScopedValue.where(name, "duchess", () -> { ScopedValue.runWhere(name, "duchess", () -> {
Thread thread = factory.newThread(() -> { }); Thread thread = factory.newThread(() -> { });
assertThrows(StructureViolationException.class, () -> flock.start(thread)); 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.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole; 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 * Tests ScopedValue
@ -40,10 +41,9 @@ import static org.openjdk.bench.jdk.incubator.concurrent.ScopedValuesData.*;
@Measurement(iterations=10, time=1) @Measurement(iterations=10, time=1)
@Threads(1) @Threads(1)
@Fork(value = 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.executor=CUSTOM",
"-Djmh.blackhole.mode=COMPILER", "-Djmh.blackhole.mode=COMPILER",
"--add-modules=jdk.incubator.concurrent",
"--enable-preview"}) "--enable-preview"})
@State(Scope.Thread) @State(Scope.Thread)
@SuppressWarnings("preview") @SuppressWarnings("preview")
@ -161,12 +161,21 @@ public class ScopedValues {
} }
// Test 4: The cost of binding, but not using any result // Test 4: The cost of binding, but not using any result
@Benchmark @Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS) @OutputTimeUnit(TimeUnit.NANOSECONDS)
public Object bind_ScopedValue() throws Exception { 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 @Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS) @OutputTimeUnit(TimeUnit.NANOSECONDS)

View File

@ -21,9 +21,8 @@
* questions. * 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.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -21,8 +21,7 @@
* questions. * questions.
*/ */
package org.openjdk.bench.java.lang;
package org.openjdk.bench.jdk.incubator.concurrent;
import java.util.concurrent.*; import java.util.concurrent.*;