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:
parent
a08c5cb3f1
commit
f1c7afcc3f
@ -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 \
|
||||||
|
@ -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 \
|
||||||
|
@ -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") \
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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));
|
||||||
}
|
}
|
@ -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
@ -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;
|
@ -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"));
|
||||||
});
|
});
|
File diff suppressed because it is too large
Load Diff
@ -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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
});
|
});
|
||||||
|
@ -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)
|
@ -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;
|
||||||
|
|
@ -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.*;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user