diff --git a/src/java.base/share/classes/java/lang/ScopedValue.java b/src/java.base/share/classes/java/lang/ScopedValue.java
index cd45818dff6..635fadfa8c8 100644
--- a/src/java.base/share/classes/java/lang/ScopedValue.java
+++ b/src/java.base/share/classes/java/lang/ScopedValue.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -29,7 +29,6 @@ package java.lang;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.lang.ref.Reference;
-import java.util.concurrent.Callable;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructureViolationException;
import java.util.function.Supplier;
@@ -64,18 +63,19 @@ import sun.security.action.GetPropertyAction;
* execution of the methods define a dynamic scope. Code in these methods with
* access to the {@code ScopedValue} object may read its value. The {@code ScopedValue}
* object reverts to being unbound 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.
+ * with an exception. The {@code ScopedValue} API supports executing a {@link Runnable},
+ * or {@link CallableOp} with a {@code ScopedValue} bound to a value.
*
*
Consider the following example with a scoped value "{@code NAME}" bound to the value
- * "{@code duke}" for the execution of a {@code run} method. The {@code run} method, in
- * turn, invokes {@code doSomething}.
+ * "{@code duke}" for the execution of a {@code Runnable}'s {@code run} method.
+ * The {@code run} method, in turn, invokes a method {@code doSomething}.
+ *
+ *
* {@snippet lang=java :
* // @link substring="newInstance" target="#newInstance" :
* private static final ScopedValue NAME = ScopedValue.newInstance();
*
- * // @link substring="runWhere" target="#runWhere" :
+ * // @link substring="runWhere" target="#runWhere(ScopedValue, Object, Runnable)" :
* ScopedValue.runWhere(NAME, "duke", () -> doSomething());
* }
* Code executed directly or indirectly by {@code doSomething}, with access to the field
@@ -84,9 +84,8 @@ import sun.security.action.GetPropertyAction;
* the {@code run} method completes.
*
*
The example using {@code runWhere} invokes a method that does not return a result.
- * The {@link #callWhere(ScopedValue, Object, Callable) callWhere} and {@link
- * #getWhere(ScopedValue, Object, Supplier) getWhere} can be used to invoke a method that
- * returns a result.
+ * The {@link #callWhere(ScopedValue, Object, CallableOp) callWhere} method can be used
+ * to invoke a method that returns a result.
* In addition, {@code ScopedValue} defines the {@link #where(ScopedValue, Object)} method
* for cases where multiple mappings (of {@code ScopedValue} to value) are accumulated
* in advance of calling a method with all {@code ScopedValue}s bound to their value.
@@ -143,7 +142,7 @@ import sun.security.action.GetPropertyAction;
* period of execution by a parent thread. When using a {@link StructuredTaskScope},
* scoped value bindings are captured when creating a {@code StructuredTaskScope}
* and inherited by all threads started in that task scope with the
- * {@link StructuredTaskScope#fork(Callable) fork} method.
+ * {@link StructuredTaskScope#fork(java.util.concurrent.Callable) fork} method.
*
*
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.
@@ -291,8 +290,8 @@ public final class ScopedValue {
/**
* A mapping of scoped values, as keys, to values.
*
- *
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
+ *
A {@code Carrier} is used to accumulate mappings so that an operation (a {@link
+ * Runnable} or {@link CallableOp}) can be executed with all scoped values in the
* mapping bound to values. The following example runs an operation with {@code k1}
* bound (or rebound) to {@code v1}, and {@code k2} bound (or rebound) to {@code v2}.
* {@snippet lang=java :
@@ -383,7 +382,7 @@ public final class ScopedValue {
carrier = carrier.prev) {
if (carrier.getKey() == key) {
Object value = carrier.get();
- return (T)value;
+ return (T) value;
}
}
throw new NoSuchElementException();
@@ -406,12 +405,14 @@ public final class ScopedValue {
*
* @param op the operation to run
* @param the type of the result of the operation
+ * @param type of the exception thrown by the operation
* @return the result
* @throws StructureViolationException if a structure violation is detected
- * @throws Exception if {@code op} completes with an exception
- * @see ScopedValue#callWhere(ScopedValue, Object, Callable)
+ * @throws X if {@code op} completes with an exception
+ * @see ScopedValue#callWhere(ScopedValue, Object, CallableOp)
+ * @since 23
*/
- public R call(Callable extends R> op) throws Exception {
+ public R call(CallableOp extends R, X> op) throws X {
Objects.requireNonNull(op);
Cache.invalidate(bitmask);
var prevSnapshot = scopedValueBindings();
@@ -419,49 +420,6 @@ public final class ScopedValue {
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. If {@code op} completes with an
- * exception then it propagated by this method.
- *
- *
Scoped values are intended to be used in a structured manner. If code
- * invoked directly or indirectly by the operation creates a {@link StructuredTaskScope}
- * but does not {@linkplain StructuredTaskScope#close() close} it, then it is detected
- * as a structure violation when the operation completes (normally or with an
- * exception). In that case, the underlying construct of the {@code StructuredTaskScope}
- * is closed and {@link StructureViolationException} is thrown.
- *
- * @param op the operation to run
- * @param the type of the result of the operation
- * @return the result
- * @throws StructureViolationException if a structure violation is detected
- * @see ScopedValue#getWhere(ScopedValue, Object, Supplier)
- */
- public 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(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 implements Callable {
- 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.
*
@@ -471,7 +429,7 @@ public final class ScopedValue {
*/
@Hidden
@ForceInline
- private R runWith(Snapshot newSnapshot, Callable op) {
+ private R runWith(Snapshot newSnapshot, CallableOp op) {
try {
Thread.setScopedValueBindings(newSnapshot);
Thread.ensureMaterializedForStackWalk(newSnapshot);
@@ -532,6 +490,24 @@ public final class ScopedValue {
}
}
+ /**
+ * An operation that returns a result and may throw an exception.
+ *
+ * @param result type of the operation
+ * @param type of the exception thrown by the operation
+ * @since 23
+ */
+ @PreviewFeature(feature = PreviewFeature.Feature.SCOPED_VALUES)
+ @FunctionalInterface
+ public interface CallableOp {
+ /**
+ * Executes this operation.
+ * @return the result, can be null
+ * @throws X if the operation completes with an exception
+ */
+ T call() throws X;
+ }
+
/**
* Creates a new {@code Carrier} with a single mapping of a {@code ScopedValue}
* key to a value. The {@code Carrier} can be used to accumulate mappings so
@@ -569,60 +545,29 @@ public final class ScopedValue {
* @implNote
* This method is implemented to be equivalent to:
* {@snippet lang=java :
- * // @link substring="call" target="Carrier#call(Callable)" :
+ * // @link substring="call" target="Carrier#call(CallableOp)" :
* ScopedValue.where(key, value).call(op);
* }
*
+ *
+ *
* @param key the {@code ScopedValue} key
* @param value the value, can be {@code null}
* @param the type of the value
* @param the result type
+ * @param type of the exception thrown by the operation
* @param op the operation to call
* @return the result
* @throws StructureViolationException if a structure violation is detected
- * @throws Exception if the operation completes with an exception
+ * @throws X if the operation completes with an exception
+ * @since 23
*/
- public static R callWhere(ScopedValue key,
- T value,
- Callable extends R> op) throws Exception {
+ public static R callWhere(ScopedValue key,
+ T value,
+ CallableOp extends R, X> op) throws X {
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. If {@code op}
- * completes with an exception then it propagated by this method.
- *
- *
Scoped values are intended to be used in a structured manner. If code
- * invoked directly or indirectly by the operation creates a {@link StructuredTaskScope}
- * but does not {@linkplain StructuredTaskScope#close() close} it, then it is detected
- * as a structure violation when the operation completes (normally or with an
- * exception). In that case, the underlying construct of the {@code StructuredTaskScope}
- * is closed and {@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 the type of the value
- * @param the result type
- * @param op the operation to call
- * @return the result
- * @throws StructureViolationException if a structure violation is detected
- */
- public static R getWhere(ScopedValue key,
- T value,
- Supplier extends R> op) {
- return where(key, value).get(op);
- }
-
/**
* Run an operation with a {@code ScopedValue} bound to a value in the current
* thread. When the operation completes (normally or with an exception), the
diff --git a/src/java.base/share/classes/javax/security/auth/Subject.java b/src/java.base/share/classes/javax/security/auth/Subject.java
index 203c0d163fd..277bbf434b0 100644
--- a/src/java.base/share/classes/javax/security/auth/Subject.java
+++ b/src/java.base/share/classes/javax/security/auth/Subject.java
@@ -436,7 +436,7 @@ public final class Subject implements java.io.Serializable {
Objects.requireNonNull(action);
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
try {
- return ScopedValue.callWhere(SCOPED_SUBJECT, subject, action);
+ return ScopedValue.callWhere(SCOPED_SUBJECT, subject, action::call);
} catch (Exception e) {
throw new CompletionException(e);
}
diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java
index ca63f714f34..05049effef8 100644
--- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java
+++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java
@@ -71,7 +71,7 @@ public @interface PreviewFeature {
STRING_TEMPLATES,
@JEP(number=477, title="Implicitly Declared Classes and Instance Main Methods", status="Third Preview")
IMPLICIT_CLASSES,
- @JEP(number=464, title="Scoped Values", status="Second Preview")
+ @JEP(number=481, title="Scoped Values", status="Third Preview")
SCOPED_VALUES,
@JEP(number=480, title="Structured Concurrency", status="Third Preview")
STRUCTURED_CONCURRENCY,
diff --git a/src/java.base/share/classes/jdk/internal/vm/ScopedValueContainer.java b/src/java.base/share/classes/jdk/internal/vm/ScopedValueContainer.java
index 343c308660d..1f15cfb05c9 100644
--- a/src/java.base/share/classes/jdk/internal/vm/ScopedValueContainer.java
+++ b/src/java.base/share/classes/jdk/internal/vm/ScopedValueContainer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024, 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
@@ -24,7 +24,7 @@
*/
package jdk.internal.vm;
-import java.util.concurrent.Callable;
+import java.lang.ScopedValue.CallableOp;
import java.util.concurrent.StructureViolationException;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
@@ -141,7 +141,7 @@ public class ScopedValueContainer extends StackableScope {
/**
* For use by ScopedValue to call a value returning operation in a structured context.
*/
- public static V call(Callable op) {
+ public static V call(CallableOp op) {
if (head() == null) {
// no need to push scope when stack is empty
return callWithoutScope(op);
@@ -153,7 +153,7 @@ public class ScopedValueContainer extends StackableScope {
/**
* Call an operation without a scope on the stack.
*/
- private static V callWithoutScope(Callable op) {
+ private static V callWithoutScope(CallableOp op) {
assert head() == null;
Throwable ex;
boolean atTop;
@@ -175,7 +175,7 @@ public class ScopedValueContainer extends StackableScope {
/**
* Call an operation with this scope on the stack.
*/
- private V doCall(Callable op) {
+ private V doCall(CallableOp op) {
Throwable ex;
boolean atTop;
V result;
diff --git a/test/jdk/java/lang/ScopedValue/ScopedValueAPI.java b/test/jdk/java/lang/ScopedValue/ScopedValueAPI.java
index 90f151ae8e9..615f8472e4f 100644
--- a/test/jdk/java/lang/ScopedValue/ScopedValueAPI.java
+++ b/test/jdk/java/lang/ScopedValue/ScopedValueAPI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024, 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
@@ -28,8 +28,8 @@
* @run junit ScopedValueAPI
*/
+import java.lang.ScopedValue.CallableOp;
import java.util.NoSuchElementException;
-import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
@@ -48,11 +48,11 @@ class ScopedValueAPI {
}
/**
- * Test that the run method is invoked.
+ * Test that runWhere invokes the Runnable's run method.
*/
@ParameterizedTest
@MethodSource("factories")
- void testRun(ThreadFactory factory) throws Exception {
+ void testRunWhere(ThreadFactory factory) throws Exception {
test(factory, () -> {
class Box { static boolean executed; }
ScopedValue name = ScopedValue.newInstance();
@@ -62,11 +62,11 @@ class ScopedValueAPI {
}
/**
- * Test the run method throwing an exception.
+ * Test runWhere when the run method throws an exception.
*/
@ParameterizedTest
@MethodSource("factories")
- void testRunThrows(ThreadFactory factory) throws Exception {
+ void testRunWhereThrows(ThreadFactory factory) throws Exception {
test(factory, () -> {
class FooException extends RuntimeException { }
ScopedValue name = ScopedValue.newInstance();
@@ -77,11 +77,11 @@ class ScopedValueAPI {
}
/**
- * Test that the call method is invoked.
+ * Test that callWhere invokes the CallableOp's call method.
*/
@ParameterizedTest
@MethodSource("factories")
- void testCall(ThreadFactory factory) throws Exception {
+ void testCallWhere(ThreadFactory factory) throws Exception {
test(factory, () -> {
ScopedValue name = ScopedValue.newInstance();
String result = ScopedValue.callWhere(name, "duke", name::get);
@@ -90,48 +90,20 @@ class ScopedValueAPI {
}
/**
- * Test that the get method is invoked.
+ * Test callWhere when the call method throws an exception.
*/
@ParameterizedTest
@MethodSource("factories")
- void testGetWhere(ThreadFactory factory) throws Exception {
- test(factory, () -> {
- ScopedValue name = ScopedValue.newInstance();
- String result = ScopedValue.getWhere(name, "duke", (Supplier)(name::get));
- assertEquals("duke", result);
- });
- }
-
- /**
- * Test the call method throwing an exception.
- */
- @ParameterizedTest
- @MethodSource("factories")
- void testCallThrows(ThreadFactory factory) throws Exception {
+ void testCallWhereThrows(ThreadFactory factory) throws Exception {
test(factory, () -> {
class FooException extends RuntimeException { }
ScopedValue name = ScopedValue.newInstance();
- Callable op = () -> { throw new FooException(); };
+ CallableOp op = () -> { throw new FooException(); };
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 name = ScopedValue.newInstance();
- Supplier op = () -> { throw new FooException(); };
- assertThrows(FooException.class, () -> ScopedValue.getWhere(name, "duke", op));
- assertFalse(name.isBound());
- });
- }
-
/**
* Test get method.
*/
@@ -144,7 +116,7 @@ class ScopedValueAPI {
assertThrows(NoSuchElementException.class, name1::get);
assertThrows(NoSuchElementException.class, name2::get);
- // run
+ // runWhere
ScopedValue.runWhere(name1, "duke", () -> {
assertEquals("duke", name1.get());
assertThrows(NoSuchElementException.class, name2::get);
@@ -153,7 +125,7 @@ class ScopedValueAPI {
assertThrows(NoSuchElementException.class, name1::get);
assertThrows(NoSuchElementException.class, name2::get);
- // call
+ // callWhere
ScopedValue.callWhere(name1, "duke", () -> {
assertEquals("duke", name1.get());
assertThrows(NoSuchElementException.class, name2::get);
@@ -161,15 +133,6 @@ class ScopedValueAPI {
});
assertThrows(NoSuchElementException.class, name1::get);
assertThrows(NoSuchElementException.class, name2::get);
-
- // get
- ScopedValue.getWhere(name1, "duke", () -> {
- assertEquals("duke", name1.get());
- assertThrows(NoSuchElementException.class, name2::get);
- return null;
- });
- assertThrows(NoSuchElementException.class, name1::get);
- assertThrows(NoSuchElementException.class, name2::get);
});
}
@@ -185,7 +148,7 @@ class ScopedValueAPI {
assertFalse(name1.isBound());
assertFalse(name2.isBound());
- // run
+ // runWhere
ScopedValue.runWhere(name1, "duke", () -> {
assertTrue(name1.isBound());
assertFalse(name2.isBound());
@@ -193,16 +156,7 @@ class ScopedValueAPI {
assertFalse(name1.isBound());
assertFalse(name2.isBound());
- // call
- ScopedValue.callWhere(name1, "duke", () -> {
- assertTrue(name1.isBound());
- assertFalse(name2.isBound());
- return null;
- });
- assertFalse(name1.isBound());
- assertFalse(name2.isBound());
-
- // call
+ // callWhere
ScopedValue.callWhere(name1, "duke", () -> {
assertTrue(name1.isBound());
assertFalse(name2.isBound());
@@ -224,13 +178,13 @@ class ScopedValueAPI {
assertNull(name.orElse(null));
assertEquals("default", name.orElse("default"));
- // run
+ // runWhere
ScopedValue.runWhere(name, "duke", () -> {
assertEquals("duke", name.orElse(null));
assertEquals("duke", name.orElse("default"));
});
- // call
+ // callWhere
ScopedValue.callWhere(name, "duke", () -> {
assertEquals("duke", name.orElse(null));
assertEquals("duke", name.orElse("default"));
@@ -250,12 +204,12 @@ class ScopedValueAPI {
ScopedValue name = ScopedValue.newInstance();
assertThrows(FooException.class, () -> name.orElseThrow(FooException::new));
- // run
+ // runWhere
ScopedValue.runWhere(name, "duke", () -> {
assertEquals("duke", name.orElseThrow(FooException::new));
});
- // call
+ // callWhere
ScopedValue.callWhere(name, "duke", () -> {
assertEquals("duke", name.orElseThrow(FooException::new));
return null;
@@ -273,7 +227,7 @@ class ScopedValueAPI {
ScopedValue name = ScopedValue.newInstance();
ScopedValue age = ScopedValue.newInstance();
- // run
+ // Carrier.run
ScopedValue.where(name, "duke").where(age, 100).run(() -> {
assertTrue(name.isBound());
assertTrue(age.isBound());
@@ -283,7 +237,7 @@ class ScopedValueAPI {
assertFalse(name.isBound());
assertFalse(age.isBound());
- // call
+ // Carrier.call
ScopedValue.where(name, "duke").where(age, 100).call(() -> {
assertTrue(name.isBound());
assertTrue(age.isBound());
@@ -293,18 +247,6 @@ class ScopedValueAPI {
});
assertFalse(name.isBound());
assertFalse(age.isBound());
-
- // get
- ScopedValue.where(name, "duke").where(age, 100).get(() -> {
- assertTrue(name.isBound());
- assertTrue(age.isBound());
- assertEquals("duke", name.get());
- assertEquals(100, (int) age.get());
- return null;
- });
- assertFalse(name.isBound());
- assertFalse(age.isBound());
-
});
}
@@ -317,7 +259,7 @@ class ScopedValueAPI {
test(factory, () -> {
ScopedValue name = ScopedValue.newInstance();
- // run
+ // runWhere
ScopedValue.runWhere(name, "duke", () -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
@@ -332,7 +274,7 @@ class ScopedValueAPI {
});
assertFalse(name.isBound());
- // call
+ // callWhere
ScopedValue.callWhere(name, "duke", () -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
@@ -348,23 +290,6 @@ class ScopedValueAPI {
return null;
});
assertFalse(name.isBound());
-
- // get
- ScopedValue.getWhere(name, "duke", () -> {
- assertTrue(name.isBound());
- assertEquals("duke", name.get());
-
- ScopedValue.where(name, "duchess").get(() -> {
- assertTrue(name.isBound());
- assertEquals("duchess", name.get());
- return null;
- });
-
- assertTrue(name.isBound());
- assertEquals("duke", name.get());
- return null;
- });
- assertFalse(name.isBound());
});
}
@@ -377,7 +302,7 @@ class ScopedValueAPI {
test(factory, () -> {
ScopedValue name = ScopedValue.newInstance();
- // run
+ // runWhere
ScopedValue.runWhere(name, null, () -> {
assertTrue(name.isBound());
assertNull(name.get());
@@ -392,7 +317,7 @@ class ScopedValueAPI {
});
assertFalse(name.isBound());
- // call
+ // callWhere
ScopedValue.callWhere(name, null, () -> {
assertTrue(name.isBound());
assertNull(name.get());
@@ -408,23 +333,6 @@ class ScopedValueAPI {
return null;
});
assertFalse(name.isBound());
-
- // getWhere
- ScopedValue.getWhere(name, null, () -> {
- assertTrue(name.isBound());
- assertNull(name.get());
-
- ScopedValue.getWhere(name, "duchess", () -> {
- assertTrue(name.isBound());
- assertTrue("duchess".equals(name.get()));
- return null;
- });
-
- assertTrue(name.isBound());
- assertNull(name.get());
- return null;
- });
- assertFalse(name.isBound());
});
}
@@ -437,7 +345,7 @@ class ScopedValueAPI {
test(factory, () -> {
ScopedValue name = ScopedValue.newInstance();
- // run
+ // runWhere
ScopedValue.runWhere(name, "duke", () -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
@@ -452,7 +360,7 @@ class ScopedValueAPI {
});
assertFalse(name.isBound());
- // call
+ // callWhere
ScopedValue.callWhere(name, "duke", () -> {
assertTrue(name.isBound());
assertEquals("duke", name.get());
@@ -468,23 +376,6 @@ class ScopedValueAPI {
return null;
});
assertFalse(name.isBound());
-
- // get
- ScopedValue.where(name, "duke").get(() -> {
- assertTrue(name.isBound());
- assertEquals("duke", name.get());
-
- ScopedValue.where(name, null).get(() -> {
- assertTrue(name.isBound());
- assertNull(name.get());
- return null;
- });
-
- assertTrue(name.isBound());
- assertEquals("duke", name.get());
- return null;
- });
- assertFalse(name.isBound());
});
}
@@ -517,16 +408,19 @@ class ScopedValueAPI {
void testNullPointerException() {
ScopedValue name = ScopedValue.newInstance();
- assertThrows(NullPointerException.class, () -> ScopedValue.where(null, "value"));
- assertThrows(NullPointerException.class, () -> ScopedValue.runWhere(null, "value", () -> { }));
- assertThrows(NullPointerException.class, () -> ScopedValue.getWhere(null, "value", () -> null));
+ assertThrows(NullPointerException.class, () -> ScopedValue.where(null, "duke"));
+
+ assertThrows(NullPointerException.class, () -> ScopedValue.runWhere(null, "duke", () -> { }));
+ assertThrows(NullPointerException.class, () -> ScopedValue.runWhere(name, "duke", null));
+
+ assertThrows(NullPointerException.class, () -> ScopedValue.callWhere(null, "duke", () -> ""));
+ assertThrows(NullPointerException.class, () -> ScopedValue.callWhere(name, "duke", null));
assertThrows(NullPointerException.class, () -> name.orElseThrow(null));
var carrier = ScopedValue.where(name, "duke");
- assertThrows(NullPointerException.class, () -> carrier.where(null, "value"));
+ assertThrows(NullPointerException.class, () -> carrier.where(null, "duke"));
assertThrows(NullPointerException.class, () -> carrier.get((ScopedValue>)null));
- assertThrows(NullPointerException.class, () -> carrier.get((Supplier>)null));
assertThrows(NullPointerException.class, () -> carrier.run(null));
assertThrows(NullPointerException.class, () -> carrier.call(null));
}
diff --git a/test/jdk/java/lang/ScopedValue/StressStackOverflow.java b/test/jdk/java/lang/ScopedValue/StressStackOverflow.java
index 863ad189c35..83813745783 100644
--- a/test/jdk/java/lang/ScopedValue/StressStackOverflow.java
+++ b/test/jdk/java/lang/ScopedValue/StressStackOverflow.java
@@ -47,8 +47,8 @@
* @run main/othervm/timeout=300 -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations StressStackOverflow
*/
+import java.lang.ScopedValue.CallableOp;
import java.time.Duration;
-import java.util.concurrent.Callable;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.StructureViolationException;
import java.util.concurrent.StructuredTaskScope;
@@ -68,12 +68,12 @@ public class StressStackOverflow {
static final long DURATION_IN_NANOS = Duration.ofMinutes(1).toNanos();
- // Test the ScopedValue recovery mechanism for stack overflows. We implement both Callable
+ // Test the ScopedValue recovery mechanism for stack overflows. We implement both CallableOp
// and Runnable interfaces. Which one gets tested depends on the constructor argument.
- class DeepRecursion implements Callable