diff --git a/src/java.base/share/classes/java/lang/StableValue.java b/src/java.base/share/classes/java/lang/StableValue.java new file mode 100644 index 0000000000000..cac1bcfe4a957 --- /dev/null +++ b/src/java.base/share/classes/java/lang/StableValue.java @@ -0,0 +1,613 @@ +/* + * Copyright (c) 2024, 2025, 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 java.lang; + +import jdk.internal.javac.PreviewFeature; +import jdk.internal.lang.stable.StableValueImpl; +import jdk.internal.lang.stable.StableValueFactories; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.RandomAccess; +import java.util.Set; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; + +/** + * A stable value is a shallowly immutable holder of deferred content. + *

+ * A {@linkplain StableValue {@code StableValue}} can be created using the factory + * method {@linkplain StableValue#of() {@code StableValue.of()}}. When created + * this way, the stable value is unset, which means it holds no content. + * Its content, of type {@code T}, can be set by calling + * {@linkplain #trySet(Object) trySet()}, {@linkplain #setOrThrow(Object) setOrThrow()}, + * or {@linkplain #orElseSet(Supplier) orElseSet()}. Once set, the content + * can never change and can be retrieved by calling {@linkplain #orElseThrow() orElseThrow()} + * , {@linkplain #orElse(Object) orElse()}, or {@linkplain #orElseSet(Supplier) orElseSet()}. + *

+ * A stable value that is set is treated as a constant by the JVM, enabling the + * same performance optimizations that are available for {@code final} fields. + * As such, stable values can be used to replace {@code final} fields in cases where + * at-most-once update semantics is crucial, but where the eager initialization + * semantics associated with {@code final} fields is too restrictive. + *

+ * Consider the following example where a stable value field "{@code logger}" is a + * shallowly immutable holder of content of type {@code Logger} and that is initially + * created as unset, which means it holds no content. Later in the example, the + * state of the "{@code logger}" field is checked and if it is still unset, + * the content is set: + * + * {@snippet lang = java: + * class Component { + * + * // Creates a new unset stable value with no content + * // @link substring="of" target="#of" : + * private final StableValue logger = StableValue.of(); + * + * Logger getLogger() { + * if (!logger.isSet()) { + * logger.trySet(Logger.create(Component.class)); + * } + * return logger.orThrow(); + * } + * + * void process() { + * logger.get().info("Process started"); + * // ... + * } + * } + *} + *

+ * Note that the holder value can only be set at most once. + *

+ * To guarantee that, even under races, only one instance of {@code Logger} is ever + * created, the {@linkplain #orElseSet(Supplier) orElseSet()} method can be used + * instead, where the content is atomically and lazily computed via a + * {@linkplain Supplier supplier}. In the example below, the supplier is provided in the + * form of a lambda expression: + * + * {@snippet lang = java: + * class Component { + * + * // Creates a new unset stable value with no content + * // @link substring="of" target="#of" : + * private final StableValue logger = StableValue.of(); + * + * Logger getLogger() { + * return logger.orElseSet( () -> Logger.create(Component.class) ); + * } + * + * void process() { + * logger.get().info("Process started"); + * // ... + * } + * } + *} + *

+ * The {@code getLogger()} method calls {@code logger.orElseSet()} on the stable value to + * retrieve its content. If the stable value is unset, then {@code orElseSet()} + * evaluates and sets the content; the content is then returned to the client. In other + * words, {@code orElseSet()} guarantees that a stable value's content is set + * before it is used. + *

+ * Furthermore, {@code orElseSet()} guarantees that the supplier provided is + * evaluated only once, even when {@code logger.orElseSet()} is invoked concurrently. + * This property is crucial as evaluation of the supplier may have side effects, + * e.g., the call above to {@code Logger.getLogger()} may result in storage resources + * being prepared. + * + *

Stable Functions

+ * Stable values provide the foundation for higher-level functional abstractions. A + * stable supplier is a supplier that computes a value and then caches it into + * a backing stable value storage for later use. A stable supplier is created via the + * {@linkplain StableValue#supplier(Supplier) StableValue.supplier()} factory, by + * providing an original {@linkplain Supplier} which is invoked when the stable supplier + * is first accessed: + * + * {@snippet lang = java: + * class Component { + * + * private final Supplier logger = + * // @link substring="supplier" target="#supplier(Supplier)" : + * StableValue.supplier( () -> Logger.getLogger(Component.class) ); + * + * void process() { + * logger.get().info("Process started"); + * // ... + * } + * } + *} + * A stable supplier encapsulates access to its backing stable value storage. This means + * that code inside {@code Component} can obtain the logger object directly from the + * stable supplier, without having to go through an accessor method like {@code getLogger()}. + *

+ * A stable int function is a function that takes an {@code int} parameter and + * uses it to compute a result that is then cached by the backing stable value storage + * for that parameter value. A stable int function is created via the + * {@linkplain StableValue#intFunction(int, IntFunction) StableValue.intFunction()} + * factory. Upon creation, the input range (i.e. [0, size)) is specified together with + * an original {@linkplain IntFunction} which is invoked at most once per input value. In + * effect, the stable int function will act like a cache for the original {@linkplain IntFunction}: + * + * {@snippet lang = java: + * class SqrtUtil { + * + * private static final IntFunction SQRT = + * // @link substring="intFunction" target="#intFunction(int,IntFunction)" : + * StableValue.intFunction(10, StrictMath::sqrt); + * + * double sqrt9() { + * return SQRT.apply(9); // May eventually constant fold to 3.0 at runtime + * } + * + * } + *} + *

+ * A stable function is a function that takes a parameter (of type {@code T}) and + * uses it to compute a result (of type {@code R}) that is then cached into a backing + * stable value storage for that parameter value. A stable function is created via the + * {@linkplain StableValue#function(Set, Function) StableValue.function()} factory. + * Upon creation, the input {@linkplain Set} is specified together with an original + * {@linkplain Function} which is invoked at most once per input value. In effect, the + * stable function will act like a cache for the original {@linkplain Function}: + * + * {@snippet lang = java: + * class SqrtUtil { + * + * private static final Function SQRT = + * // @link substring="function" target="#function(Set,Function)" : + * StableValue.function(Set.of(1, 2, 4, 8, 16, 32), StrictMath::sqrt); + * + * double sqrt16() { + * return SQRT.apply(16); // May eventually constant fold to 4.0 at runtime + * } + * + * } + *} + * + *

Stable Collections

+ * Stable values can also be used as backing storage for + * {@linkplain Collection##unmodifiable unmodifiable collections}. A stable list + * is an unmodifiable list, backed by an array of stable values. The stable list elements + * are computed when they are first accessed, using a provided {@linkplain IntFunction}: + * + * {@snippet lang = java: + * class SqrtUtil { + * + * private static final List SQRT = + * // @link substring="list" target="#list(int,IntFunction)" : + * StableValue.list(10, StrictMath::sqrt); + * + * double sqrt9() { + * return SQRT.apply(9); // May eventually constant fold to 3.0 at runtime + * } + * + * } + *} + *

+ * Similarly, a stable map is an unmodifiable map whose keys are known at + * construction. The stable map values are computed when they are first accessed, + * using a provided {@linkplain Function}: + * + * {@snippet lang = java: + * class SqrtUtil { + * + * private static final Map SQRT = + * // @link substring="map" target="#map(Set,Function)" : + * StableValue.map(Set.of(1, 2, 4, 8, 16, 32), StrictMath::sqrt); + * + * double sqrt16() { + * return SQRT.apply(16); // May eventually constant fold to 4.0 at runtime + * } + * + * } + *} + * + *

Composing stable values

+ * A stable value can depend on other stable values, thereby creating a dependency graph + * that can be lazily computed but where access to individual elements still can be + * constant-folded. In the following example, a single {@code Foo} and a {@code Bar} + * instance (that is dependent on the {@code Foo} instance) are lazily created, both of + * which are held by stable values: + * {@snippet lang = java: + * class Dependency { + * + * public static class Foo { + * // ... + * } + * + * public static class Bar { + * public Bar(Foo foo) { + * // ... + * } + * } + * + * private static final Supplier FOO = StableValue.supplier(Foo::new); + * private static final Supplier BAR = StableValue.supplier(() -> new Bar(FOO.get())); + * + * public static Foo foo() { + * return FOO.get(); + * } + * + * public static Bar bar() { + * return BAR.get(); + * } + * + * } + *} + * Calling {@code bar()} will create the {@code Bar} singleton if it is not already + * created. Upon such a creation, the dependent {@code Foo} will first be created if + * the {@code Foo} does not already exist. + *

+ * Here is another example where a more complex dependency graph is created in which + * integers in the Fibonacci delta series are lazily computed: + * {@snippet lang = java: + * class Fibonacci { + * + * private static final int MAX_SIZE_INT = 46; + * + * private static final IntFunction FIB = + * StableValue.intFunction(MAX_SIZE_INT, Fibonacci::fib); + * + * public static int fib(int n) { + * return n < 2 + * ? n + * : FIB.apply(n - 1) + FIB.apply(n - 2); + * } + * + * } + *} + * Both {@code FIB} and {@code Fibonacci::fib} recurses into each other. Because the + * stable int function {@code FIB} caches intermediate results, the initial + * computational complexity is reduced from exponential to linear compared to a + * traditional non-caching recursive fibonacci method. Once computed, the VM is free to + * constant-fold expressions like {@code Fibonacci.fib(10)}. + *

+ * The fibonacci example above is a dependency graph with no circular dependencies (i.e., + * it is a dependency tree). If there are circular dependencies in a dependency graph, + * a stable value will eventually throw a {@linkplain StackOverflowError} upon referencing + * elements in a circularity. + * + *

Thread Safety

+ * The content of a stable value is guaranteed to be set at most once. If competing + * threads are racing to set a stable value, only one update succeeds, while other updates + * are blocked until the stable value becomes set. + *

+ * The at-most-once write operation on a stable value that succeeds + * (e.g. {@linkplain #trySet(Object) trySet()}) + * happens-before + * any subsequent read operation (e.g. {@linkplain #orElseThrow()}). + *

+ * The method {@linkplain StableValue#orElseSet(Supplier) orElseSet()} guarantees that + * the provided {@linkplain Supplier} is invoked successfully at most once even under + * race. Since stable functions and stable collections are built on top of + * {@linkplain StableValue#orElseSet(Supplier) orElseSet()} they too are + * thread safe and guarantee at-most-once-per-input invocation. + * + *

Miscellaneous

+ * Except for a StableValue's content itself, all method parameters must be + * non-null or a {@link NullPointerException} will be thrown. + *

+ * Stable functions and collections are not {@link Serializable} as this would require + * {@linkplain #list(int, IntFunction) mappers} to be {@link Serializable} as well, + * which would introduce security vulnerabilities. + *

+ * As objects can be set via stable values but never removed, this can be a source + * of unintended memory leaks. A stable value's content is + * {@linkplain java.lang.ref##reachability strongly reachable}. Clients are advised that + * {@linkplain java.lang.ref##reachability reachable} stable values will hold their set + * content perpetually. + * + * @implSpec Implementing classes of {@linkplain StableValue} are free to synchronize on + * {@code this} and consequently, care should be taken whenever + * (directly or indirectly) synchronizing on a {@code StableValue}. Failure to + * do this may lead to deadlock. Stable functions and collections on the + * other hand are guaranteed not to synchronize on {@code this}. + * + * @implNote A {@linkplain StableValue} is mainly intended to be a non-public field in + * a class and is usually neither exposed directly via accessors nor passed as + * a method parameter. + * + * @param type of the content + * + * @since 25 + */ +@PreviewFeature(feature = PreviewFeature.Feature.STABLE_VALUES) +public sealed interface StableValue + permits StableValueImpl { + + // Principal methods + + /** + * {@return {@code true} if the content was set to the provided {@code value}, + * {@code false} otherwise} + *

+ * When this method returns, the content is always set. + * + * @param value to set + */ + boolean trySet(T value); + + /** + * {@return the content if set, otherwise, returns the provided {@code other} value} + * + * @param other to return if the content is not set + */ + T orElse(T other); + + /** + * {@return the content if set, otherwise, throws {@code NoSuchElementException}} + * + * @throws NoSuchElementException if no content is set + */ + T orElseThrow(); + + /** + * {@return {@code true} if the content is set, {@code false} otherwise} + */ + boolean isSet(); + + /** + * {@return the content; if unset, first attempts to compute and set the + * content using the provided {@code supplier}} + *

+ * The provided {@code supplier} is guaranteed to be invoked at most once if it + * completes without throwing an exception. + *

+ * If the supplier throws an (unchecked) exception, the exception is rethrown, and no + * content is set. The most common usage is to construct a new object serving + * as a lazily computed value or memoized result, as in: + * + * {@snippet lang=java: + * Value witness = stable.orElseSet(Value::new); + * } + *

+ * When this method returns successfully, the content is always set. + * + * @implSpec The implementation logic is equivalent to the following steps for this + * {@code stable}: + * + * {@snippet lang=java: + * if (stable.isSet()) { + * return stable.get(); + * } else { + * V newValue = supplier.get(); + * stable.setOrThrow(newValue); + * return newValue; + * } + * } + * Except it is thread-safe and will only return the same witness value + * regardless if invoked by several threads. Also, the provided {@code supplier} + * will only be invoked once even if invoked from several threads unless the + * {@code supplier} throws an exception. + * + * @param supplier to be used for computing the content, if not previously set + */ + T orElseSet(Supplier supplier); + + // Convenience methods + + /** + * Sets the cintent to the provided {@code value}, or, if already set, + * throws {@code IllegalStateException}. + *

+ * When this method returns (or throws an exception), the content is always set. + * + * @param value to set + * @throws IllegalStateException if the content was already set + */ + void setOrThrow(T value); + + // Object methods + + /** + * {@return {@code true} if {@code this == obj}, {@code false} otherwise} + * + * @param obj to check for equality + */ + boolean equals(Object obj); + + /** + * {@return the {@linkplain System#identityHashCode(Object) identity hash code} of + * {@code this} object} + */ + int hashCode(); + + // Factories + + /** + * {@return a new unset stable value} + *

+ * An unset stable value has no content. + * + * @param type of the content + */ + static StableValue of() { + return StableValueFactories.of(); + } + + /** + * {@return a new pre-set stable value with the provided {@code content}} + * + * @param content to set + * @param type of the content + */ + static StableValue of(T content) { + return StableValueFactories.of(content); + } + + /** + * {@return a new unset stable supplier} + *

+ * The returned {@linkplain Supplier supplier} is a caching supplier that records + * the value of the provided {@code original} supplier upon being first accessed via + * the returned supplier's {@linkplain Supplier#get() get()} method. + *

+ * The provided {@code original} supplier is guaranteed to be successfully invoked + * at most once even in a multi-threaded environment. Competing threads invoking the + * returned supplier's {@linkplain Supplier#get() get()} method when a value is + * already under computation will block until a value is computed or an exception is + * thrown by the computing thread. + *

+ * If the provided {@code original} supplier throws an exception, it is relayed + * to the initial caller and no content is recorded. + * + * @param original supplier used to compute a cached value + * @param the type of results supplied by the returned supplier + */ + static Supplier supplier(Supplier original) { + Objects.requireNonNull(original); + return StableValueFactories.supplier(original); + } + + /** + * {@return a new unset stable int function} + *

+ * The returned {@link IntFunction int function} is a caching int function that, + * for each allowed input, records the values of the provided {@code original} + * int function upon being first accessed via the returned int function's + * {@linkplain IntFunction#apply(int) apply()} method. + *

+ * The provided {@code original} int function is guaranteed to be successfully invoked + * at most once per allowed input, even in a multi-threaded environment. Competing + * threads invoking the returned int function's + * {@linkplain IntFunction#apply(int) apply()} method when a value is already under + * computation will block until a value is computed or an exception is thrown by + * the computing thread. + *

+ * If the provided {@code original} int function throws an exception, it is relayed + * to the initial caller and no content is recorded. + * + * @param size the size of the allowed inputs in {@code [0, size)} + * @param original IntFunction used to compute cached values + * @param the type of results delivered by the returned IntFunction + */ + static IntFunction intFunction(int size, + IntFunction original) { + if (size < 0) { + throw new IllegalArgumentException(); + } + Objects.requireNonNull(original); + return StableValueFactories.intFunction(size, original); + } + + /** + * {@return a new unset stable function} + *

+ * The returned {@link Function function} is a caching function that, for each allowed + * input in the given set of {@code inputs}, records the values of the provided + * {@code original} function upon being first accessed via the returned function's + * {@linkplain Function#apply(Object) apply()} method. + *

+ * The provided {@code original} function is guaranteed to be successfully invoked + * at most once per allowed input, even in a multi-threaded environment. Competing + * threads invoking the returned function's {@linkplain Function#apply(Object) apply()} + * method when a value is already under computation will block until a value is + * computed or an exception is thrown by the computing thread. + *

+ * If the provided {@code original} function throws an exception, it is relayed to + * the initial caller and no content is recorded. + * + * @param inputs the set of allowed input values + * @param original Function used to compute cached values + * @param the type of the input to the returned Function + * @param the type of results delivered by the returned Function + */ + static Function function(Set inputs, + Function original) { + Objects.requireNonNull(inputs); + Objects.requireNonNull(original); + return StableValueFactories.function(inputs, original); + } + + /** + * {@return a new unset stable list with the provided {@code size}} + *

+ * The returned list is an {@linkplain Collection##unmodifiable unmodifiable} list + * whose size is known at construction. The list's elements are computed via the + * provided {@code mapper} when they are first accessed + * (e.g. via {@linkplain List#get(int) List::get}). + *

+ * The provided {@code mapper} int function is guaranteed to be successfully invoked + * at most once per list index, even in a multi-threaded environment. Competing + * threads accessing an element already under computation will block until an element + * is computed or an exception is thrown by the computing thread. + *

+ * If the provided {@code mapper} throws an exception, it is relayed to the initial + * caller and no value for the element is recorded. + *

+ * The returned list and its {@link List#subList(int, int) subList} views implement + * the {@link RandomAccess} interface. + * + * @param size the size of the returned list + * @param mapper to invoke whenever an element is first accessed + * (may return {@code null}) + * @param the type of elements in the returned list + */ + static List list(int size, + IntFunction mapper) { + if (size < 0) { + throw new IllegalArgumentException(); + } + Objects.requireNonNull(mapper); + return StableValueFactories.list(size, mapper); + } + + /** + * {@return a new unset stable map with the provided {@code keys}} + *

+ * The returned map is an {@linkplain Collection##unmodifiable unmodifiable} map whose + * keys are known at construction. The map's values are computed via the provided + * {@code mapper} when they are first accessed + * (e.g. via {@linkplain Map#get(Object) Map::get}). + *

+ * The provided {@code mapper} function is guaranteed to be successfully invoked + * at most once per key, even in a multi-threaded environment. Competing + * threads accessing a value already under computation will block until an element + * is computed or an exception is thrown by the computing thread. + *

+ * If the provided {@code mapper} throws an exception, it is relayed to the initial + * caller and no value associated with the provided key is recorded. + * + * @param keys the keys in the returned map + * @param mapper to invoke whenever an associated value is first accessed + * (may return {@code null}) + * @param the type of keys maintained by the returned map + * @param the type of mapped values in the returned map + */ + static Map map(Set keys, + Function mapper) { + Objects.requireNonNull(keys); + Objects.requireNonNull(mapper); + return StableValueFactories.map(keys, mapper); + } + +}