From 707154235b29bebc4c3fdb797e24acd8e9f6916a Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Thu, 30 May 2024 15:41:56 +0000 Subject: [PATCH] 8331189: Implementation of Scoped Values (Third Preview) Reviewed-by: aph, jpai, mcimadamore --- .../share/classes/java/lang/ScopedValue.java | 149 +++++---------- .../classes/javax/security/auth/Subject.java | 2 +- .../jdk/internal/javac/PreviewFeature.java | 2 +- .../jdk/internal/vm/ScopedValueContainer.java | 10 +- .../java/lang/ScopedValue/ScopedValueAPI.java | 176 ++++-------------- .../lang/ScopedValue/StressStackOverflow.java | 9 +- .../openjdk/bench/java/lang/ScopedValues.java | 16 +- 7 files changed, 96 insertions(+), 268 deletions(-) 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 op) throws Exception { + public R call(CallableOp 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 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 s; - CallableAdapter(Supplier 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 op) throws Exception { + public static R callWhere(ScopedValue key, + T value, + CallableOp 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 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, Supplier, Runnable { + class DeepRecursion implements CallableOp, Supplier, Runnable { enum Behaviour { - CALL, GET, RUN; + CALL, RUN; private static final Behaviour[] values = values(); public static Behaviour choose(ThreadLocalRandom tlr) { return values[tlr.nextInt(3)]; @@ -97,7 +97,6 @@ public class StressStackOverflow { try { switch (behaviour) { case CALL -> ScopedValue.where(el, el.get() + 1).call(() -> fibonacci_pad(20, this)); - case GET -> ScopedValue.where(el, el.get() + 1).get(() -> fibonacci_pad(20, this)); case RUN -> ScopedValue.where(el, el.get() + 1).run(() -> fibonacci_pad(20, this)); } if (!last.equals(el.get())) { diff --git a/test/micro/org/openjdk/bench/java/lang/ScopedValues.java b/test/micro/org/openjdk/bench/java/lang/ScopedValues.java index 69ce3d63fe4..f11bc805b7f 100644 --- a/test/micro/org/openjdk/bench/java/lang/ScopedValues.java +++ b/test/micro/org/openjdk/bench/java/lang/ScopedValues.java @@ -24,9 +24,8 @@ package org.openjdk.bench.java.lang; -import java.util.concurrent.Callable; +import java.lang.ScopedValue.CallableOp; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; @@ -164,18 +163,9 @@ public class ScopedValues { @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public Object bind_ScopedValue() throws Exception { - return HOLD_42.call(aCallable); + return HOLD_42.call(aCallableOp); } - private static final Callable> 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> aSupplier = () -> ScopedValues.class; + private static final CallableOp, RuntimeException> aCallableOp = () -> ScopedValues.class; @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS)