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..37ffd686310ef --- /dev/null +++ b/src/java.base/share/classes/java/lang/StableValue.java @@ -0,0 +1,498 @@ +/* + * Copyright (c) 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 + * 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.access.SharedSecrets; +import jdk.internal.javac.PreviewFeature; +import jdk.internal.lang.stable.StableValueImpl; +import jdk.internal.lang.stable.StableValueUtil; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ThreadFactory; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; + +/** + * A thin, atomic, non-blocking, thread-safe, set-at-most-once, stable value holder + * eligible for certain JVM optimizations if set to a value. + *
+ * A stable value is said to be monotonic because the state of a stable value can only go + * from unset to set and consequently, a value can only be set + * at most once. + *
+ * StableValue is mainly intended to be a member of a holding class and is usually neither + * exposed directly via accessors nor passed as a method parameter. + * + *
+ * To create a new fresh (unset) StableValue, use the + * {@linkplain StableValue#newInstance() StableValue::newInstance} factory. + *
+ * This class also contains a number of convenience methods for creating constructs + * involving stable values: + *
+ * The constructs above are eligible for similar JVM optimizations as StableValue + * instances. + * + *
+ * When this method returns, a holder value is always set. + * + * @param value to set (nullable) + */ + boolean trySet(T value); + + /** + * {@return the set holder value (nullable) if set, otherwise return the + * {@code other} value} + * + * @param other to return if the stable holder value is not set + */ + T orElse(T other); + + /** + * {@return the set holder value if set, otherwise throws {@code NoSuchElementException}} + * + * @throws NoSuchElementException if no value is set + */ + T orElseThrow(); + + /** + * {@return {@code true} if a holder value is set, {@code false} otherwise} + */ + boolean isSet(); + + // Convenience methods + + /** + * Sets the holder value to the provided {@code value}, or, if already set, + * throws {@link IllegalStateException}} + *
+ * When this method returns (or throws an Exception), a holder value is always set.
+ *
+ * @param value to set (nullable)
+ * @throws IllegalStateException if a holder value is already set
+ */
+ default void setOrThrow(T value) {
+ if (!trySet(value)) {
+ throw new IllegalStateException("Cannot set the holder value to " + value +
+ " because a holder value is alredy set: " + this);
+ }
+ }
+
+ // Factories
+
+ /**
+ * {@return a fresh stable value with an unset holder value}
+ *
+ * @param
+ * The provided {@code original} supplier is guaranteed to be successfully invoked
+ * at most once even in a multi-threaded environment. Competing threads invoking the
+ * {@linkplain Supplier#get() Supplier::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 {@code original} Supplier invokes the returned Supplier recursively,
+ * a StackOverflowError will be thrown when the returned
+ * Supplier's {@linkplain Supplier#get() Supplier::get} method is invoked.
+ *
+ * If the provided {@code original} supplier throws an exception, it is relayed
+ * to the initial caller.
+ *
+ * @param original supplier used to compute a memoized value
+ * @param
+ * The provided {@code original} supplier is guaranteed to be successfully invoked
+ * at most once even in a multi-threaded environment. Competing threads invoking the
+ * {@linkplain Supplier#get() Supplier::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 {@code original} Supplier invokes the returned Supplier recursively,
+ * a StackOverflowError will be thrown when the returned
+ * Supplier's {@linkplain Supplier#get() Supplier::get} method is invoked.
+ *
+ * If the provided {@code original} supplier throws an exception, it is relayed
+ * to the initial caller. If the memoized supplier is computed by a background thread,
+ * exceptions from the provided {@code original} supplier will be relayed to the
+ * background thread's {@linkplain Thread#getUncaughtExceptionHandler() uncaught
+ * exception handler}.
+ *
+ * @param original supplier used to compute a memoized value
+ * @param factory a factory that will be used to create a background thread that will
+ * attempt to compute the memoized value.
+ * @param
+ * The provided {@code original} IntFunction is guaranteed to be successfully invoked
+ * at most once per allowed input, even in a multi-threaded environment. Competing
+ * threads invoking the {@linkplain IntFunction#apply(int) IntFunction::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 {@code original} IntFunction invokes the returned IntFunction recursively
+ * for a particular input value, a StackOverflowError will be thrown when the returned
+ * IntFunction's {@linkplain IntFunction#apply(int) IntFunction::apply} method is
+ * invoked.
+ *
+ * If the provided {@code original} IntFunction throws an exception, it is relayed
+ * to the initial caller.
+ *
+ * @param size the size of the allowed inputs in {@code [0, size)}
+ * @param original IntFunction used to compute a memoized value
+ * @param
+ * The provided {@code original} IntFunction is guaranteed to be successfully invoked
+ * at most once per allowed input, even in a multi-threaded environment. Competing
+ * threads invoking the {@linkplain IntFunction#apply(int) IntFunction::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 {@code original} IntFunction invokes the returned IntFunction recursively
+ * for a particular input value, a StackOverflowError will be thrown when the returned
+ * IntFunction's {@linkplain IntFunction#apply(int) IntFunction::apply} method is
+ * invoked.
+ *
+ * If the provided {@code original} IntFunction throws an exception, it is relayed
+ * to the initial caller. If the memoized IntFunction is computed by a background
+ * thread, exceptions from the provided {@code original} IntFunction will be relayed
+ * to the background thread's {@linkplain Thread#getUncaughtExceptionHandler()
+ * uncaught exception handler}.
+ *
+ * The order in which background threads are started is unspecified.
+ *
+ * @param size the size of the allowed inputs in {@code [0, size)}
+ * @param original IntFunction used to compute a memoized value
+ * @param factory a factory that will be used to create {@code size} background
+ * threads that will attempt to compute all the memoized values.
+ * @param
+ * 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 {@linkplain Function#apply(Object) Function::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 {@code original} Function invokes the returned Function recursively
+ * for a particular input value, a StackOverflowError will be thrown when the returned
+ * Function's {@linkplain Function#apply(Object) Function::apply} method is invoked.
+ *
+ * If the provided {@code original} Function throws an exception, it is relayed
+ * to the initial caller. If the memoized Function is computed by a background
+ * thread, exceptions from the provided {@code original} Function will be relayed to
+ * the background thread's {@linkplain Thread#getUncaughtExceptionHandler() uncaught
+ * exception handler}.
+ *
+ * The order in which background threads are started is unspecified.
+ *
+ * @param inputs the set of allowed input values
+ * @param original Function used to compute a memoized value
+ * @param
+ * 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 {@linkplain Function#apply(Object) Function::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 {@code original} Function invokes the returned Function recursively
+ * for a particular input value, a StackOverflowError will be thrown when the returned
+ * Function's {@linkplain Function#apply(Object) Function::apply} method is invoked.
+ *
+ * If the provided {@code original} Function throws an exception, it is relayed
+ * to the initial caller. If the memoized Function is computed by a background
+ * thread, exceptions from the provided {@code original} Function will be relayed to
+ * the background thread's {@linkplain Thread#getUncaughtExceptionHandler() uncaught
+ * exception handler}.
+ *
+ * The order in which background threads are started is unspecified.
+ *
+ * @param inputs the set of allowed input values
+ * @param original Function used to compute a memoized value
+ * @param factory a factory that will be used to create {@code size} background
+ * threads that will attempt to compute the memoized values.
+ * @param
+ * The provided {@code mapper} IntFunction 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 {@code mapper} IntFunction invokes the returned IntFunction recursively
+ * for a particular index, a StackOverflowError will be thrown when the returned
+ * List's {@linkplain List#get(int) List::get} method is invoked.
+ *
+ * If the provided {@code mapper} IntFunction throws an exception, it is relayed
+ * to the initial caller and no element is computed.
+ *
+ * The returned List is not {@link Serializable}
+ *
+ * @param size the size of the returned list
+ * @param mapper to invoke whenever an element is first accessed (may return null)
+ * @param
+ * 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 an associated value already under computation will block until
+ * an associated value is computed or an exception is thrown by the computing thread.
+ *
+ * If the {@code mapper} Function invokes the returned Map recursively
+ * for a particular key, a StackOverflowError will be thrown when the returned
+ * Map's {@linkplain Map#get(Object) Map::get}} method is invoked.
+ *
+ * If the provided {@code mapper} Function throws an exception, it is relayed
+ * to the initial caller and no value is computed.
+ *
+ * The returned Map is not {@link Serializable}
+ *
+ * @param keys the keys in the returned map
+ * @param mapper to invoke whenever an associated value is first accessed
+ * (may return null)
+ * @param