Skip to content

Instantly share code, notes, and snippets.

@andrebreves
Last active February 21, 2025 15:20
Show Gist options
  • Save andrebreves/da26bd842e6616c6666cb2b23e240464 to your computer and use it in GitHub Desktop.
Save andrebreves/da26bd842e6616c6666cb2b23e240464 to your computer and use it in GitHub Desktop.
A partial implementation of JEP 502: Stable Values, including a method to set and stream
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Spliterator;
import static java.util.Spliterator.IMMUTABLE;
import static java.util.Spliterator.ORDERED;
import static java.util.Spliterator.SIZED;
import static java.util.Spliterator.SUBSIZED;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public final class StableValue<T> {
private volatile T value;
private volatile ReentrantLock lock;
private StableValue(T value, ReentrantLock lock) {
this.value = value;
this.lock = lock;
}
// https://openjdk.org/jeps/502
// https://bugs.openjdk.org/browse/JDK-8342068
public static <T> StableValue<T> of() {
return new StableValue<>(null, new ReentrantLock());
}
public static <T> StableValue<T> of(T value) {
return new StableValue<>(value, null);
}
public static <T> Supplier<T> supplier(Supplier<? extends T> supplier) {
Objects.requireNonNull(supplier);
final StableValue<T> value = new StableValue<>(null, new ReentrantLock());
return () -> value.orElseSet(supplier);
}
public boolean trySet(T value) {
if (lock == null) return false;
else return maybeComputeValue(() -> value) == value;
}
public void setOrThrow(T value) {
if (!trySet(value)) throw new IllegalStateException("Value already set");
}
public T orElse(T other) {
if (lock == null) return value;
else return other;
}
public T orElseThrow() {
if (lock == null) return value;
else throw new NoSuchElementException();
}
public boolean isSet() {
return lock == null;
}
public T orElseSet(Supplier<? extends T> supplier) {
Objects.requireNonNull(supplier);
if (lock == null) return value;
else return maybeComputeValue(supplier);
}
private T maybeComputeValue(Supplier<? extends T> supplier) {
final ReentrantLock _lock = lock;
if (_lock != null) {
_lock.lock();
try {
if (lock != null) {
value = supplier.get();
lock = null;
}
} catch (Throwable t) {
throw t;
} finally {
_lock.unlock();
}
}
return value;
}
private final class StableValueSpliterator implements Spliterator<T> {
private final Supplier<? extends T> supplier;
private StableValueSpliterator(Supplier<? extends T> supplier) {
this.supplier = supplier;
}
private static final int CHARACTERISTICS = IMMUTABLE | ORDERED | SIZED | SUBSIZED;
@Override
public int characteristics() {
return CHARACTERISTICS;
}
@Override
public long estimateSize() {
return 1;
}
@Override
public Spliterator<T> trySplit() {
return null;
}
private boolean done = false;
@Override
public boolean tryAdvance(Consumer<? super T> action) {
if (!done) {
if (lock == null) action.accept(value);
else action.accept(maybeComputeValue(supplier));
done = true;
return true;
} else return false;
}
}
public Stream<T> setThenStream(Supplier<? extends T> supplier) {
Objects.requireNonNull(supplier);
if (lock == null) return Stream.of(value);
else return StreamSupport.stream(new StableValueSpliterator(supplier), false);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment