8355022: Implement JEP 506: Scoped Values

Reviewed-by: liach, alanb
This commit is contained in:
Andrew Haley 2025-05-28 09:41:37 +00:00
parent f02190bc30
commit 4e1878ca45
6 changed files with 9 additions and 18 deletions

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022, Red Hat Inc.
* Copyright (c) 2020, 2025, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -34,7 +34,6 @@ import java.util.concurrent.StructureViolationException;
import java.util.function.Supplier;
import jdk.internal.access.JavaUtilConcurrentTLRAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
import jdk.internal.vm.ScopedValueContainer;
@ -237,9 +236,8 @@ import jdk.internal.vm.ScopedValueContainer;
* have to be regenerated after a blocking operation.
*
* @param <T> the type of the value
* @since 21
* @since 25
*/
@PreviewFeature(feature = PreviewFeature.Feature.SCOPED_VALUES)
public final class ScopedValue<T> {
private final int hash;
@ -310,9 +308,8 @@ public final class ScopedValue<T> {
* <p> Unless otherwise specified, passing a {@code null} argument to a method in
* this class will cause a {@link NullPointerException} to be thrown.
*
* @since 21
* @since 25
*/
@PreviewFeature(feature = PreviewFeature.Feature.SCOPED_VALUES)
public static final class Carrier {
// Bit masks: a 1 in position n indicates that this set of bound values
// hits that slot in the cache.
@ -413,7 +410,6 @@ public final class ScopedValue<T> {
* @return the result
* @throws StructureViolationException if a structure violation is detected
* @throws X if {@code op} completes with an exception
* @since 23
*/
public <R, X extends Throwable> R call(CallableOp<? extends R, X> op) throws X {
Objects.requireNonNull(op);
@ -497,9 +493,8 @@ public final class ScopedValue<T> {
*
* @param <T> result type of the operation
* @param <X> type of the exception thrown by the operation
* @since 23
* @since 25
*/
@PreviewFeature(feature = PreviewFeature.Feature.SCOPED_VALUES)
@FunctionalInterface
public interface CallableOp<T, X extends Throwable> {
/**
@ -612,10 +607,11 @@ public final class ScopedValue<T> {
* Returns the value of this scoped value if bound in the current thread, otherwise
* returns {@code other}.
*
* @param other the value to return if not bound, can be {@code null}
* @param other the value to return if not bound
* @return the value of the scoped value if bound, otherwise {@code other}
*/
public T orElse(T other) {
Objects.requireNonNull(other);
Object obj = findBinding();
if (obj != Snapshot.NIL) {
@SuppressWarnings("unchecked")

View File

@ -300,7 +300,7 @@ public final class Subject implements java.io.Serializable {
* @since 18
*/
public static Subject current() {
return SCOPED_SUBJECT.orElse(null);
return SCOPED_SUBJECT.isBound() ? SCOPED_SUBJECT.get() : null;
}
/**

View File

@ -69,7 +69,6 @@ public @interface PreviewFeature {
//---
IMPLICIT_CLASSES, //to be removed when boot JDK is 25
@JEP(number=487, title="Scoped Values", status="Fourth Preview")
SCOPED_VALUES,
@JEP(number=505, title="Structured Concurrency", status="Fifth Preview")
STRUCTURED_CONCURRENCY,

View File

@ -24,7 +24,6 @@
/*
* @test
* @summary Stress test ScopedValue with many bindings and rebindings
* @enablePreview
* @library /test/lib
* @key randomness
* @run junit ManyBindings

View File

@ -24,7 +24,6 @@
/*
* @test
* @summary Test ScopedValue API
* @enablePreview
* @run junit ScopedValueAPI
*/
@ -175,18 +174,15 @@ class ScopedValueAPI {
void testOrElse(ThreadFactory factory) throws Exception {
test(factory, () -> {
ScopedValue<String> name = ScopedValue.newInstance();
assertNull(name.orElse(null));
assertEquals("default", name.orElse("default"));
// where
ScopedValue.where(name, "duke").run(() -> {
assertEquals("duke", name.orElse(null));
assertEquals("duke", name.orElse("default"));
});
// callWhere
ScopedValue.where(name, "duke").call(() -> {
assertEquals("duke", name.orElse(null));
assertEquals("duke", name.orElse("default"));
return null;
});
@ -416,6 +412,7 @@ class ScopedValueAPI {
assertThrows(NullPointerException.class, () -> ScopedValue.where(null, "duke").call(() -> ""));
assertThrows(NullPointerException.class, () -> ScopedValue.where(name, "duke").call(null));
assertThrows(NullPointerException.class, () -> name.orElse(null));
assertThrows(NullPointerException.class, () -> name.orElseThrow(null));
var carrier = ScopedValue.where(name, "duke");
@ -423,6 +420,7 @@ class ScopedValueAPI {
assertThrows(NullPointerException.class, () -> carrier.get((ScopedValue<?>)null));
assertThrows(NullPointerException.class, () -> carrier.run(null));
assertThrows(NullPointerException.class, () -> carrier.call(null));
assertThrows(NullPointerException.class, () -> carrier.run(() -> name.orElse(null)));
}
@FunctionalInterface

View File

@ -26,7 +26,6 @@ import java.util.NoSuchElementException;
/*
* @test
* @bug 8319120
* @enablePreview
* @run main/othervm -Xmx10m UnboundValueAfterOOME
*/
public class UnboundValueAfterOOME {