138 lines
3.8 KiB
Java
138 lines
3.8 KiB
Java
package io.gitlab.jfronny.commons.concurrent;
|
|
|
|
import java.util.Deque;
|
|
import java.util.LinkedList;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.Optional;
|
|
import java.util.concurrent.Callable;
|
|
import java.util.function.Supplier;
|
|
|
|
/**
|
|
* Naive, inefficient reimplementation of JDK22 ScopedValue based on ThreadLocal.
|
|
* See the official Javadoc for more information.
|
|
* Use the JDK22 version if possible!
|
|
* Does not include ScopedValue.where
|
|
*
|
|
* @param <T> The type of the value
|
|
*/
|
|
public class ScopedValue<T> {
|
|
public static <T> ScopedValue<T> newInstance() {
|
|
return new ScopedValue<>();
|
|
}
|
|
|
|
public static <T, R> R callWhere(ScopedValue<T> key, T value, Callable<? extends R> op) throws Exception {
|
|
key.value.get().setValue(value);
|
|
try {
|
|
return op.call();
|
|
} finally {
|
|
key.value.get().clear();
|
|
}
|
|
}
|
|
|
|
public static <T, R> R getWhere(ScopedValue<T> key, T value, Supplier<? extends R> op) {
|
|
key.value.get().setValue(value);
|
|
try {
|
|
return op.get();
|
|
} finally {
|
|
key.value.get().clear();
|
|
}
|
|
}
|
|
|
|
public static <T> void runWhere(ScopedValue<T> key, T value, Runnable op) {
|
|
key.value.get().setValue(value);
|
|
try {
|
|
op.run();
|
|
} finally {
|
|
key.value.get().clear();
|
|
}
|
|
}
|
|
|
|
private final InheritableThreadLocal<Carrier<T>> value = new InheritableThreadLocal<>() {
|
|
@Override
|
|
protected Carrier<T> childValue(Carrier<T> parentValue) {
|
|
return new Carrier<>(parentValue);
|
|
}
|
|
|
|
@Override
|
|
protected Carrier<T> initialValue() {
|
|
return new Carrier<>();
|
|
}
|
|
};
|
|
|
|
public T get() {
|
|
return value.get().getValue();
|
|
}
|
|
|
|
public boolean isBound() {
|
|
return value.get().isPresent();
|
|
}
|
|
|
|
public T orElse(T other) {
|
|
return value.get().orElse(other);
|
|
}
|
|
|
|
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
|
|
return value.get().orElseThrow(exceptionSupplier);
|
|
}
|
|
|
|
private static class Carrier<T> {
|
|
private final Deque<T> value;
|
|
private final Carrier<T> parent;
|
|
|
|
public Carrier() {
|
|
this(null);
|
|
}
|
|
|
|
public Carrier(Carrier<T> parent) {
|
|
this.value = new LinkedList<>();
|
|
this.parent = parent;
|
|
}
|
|
|
|
private Optional<T> valueIfPresent() {
|
|
synchronized (value) {
|
|
if (!value.isEmpty()) return Optional.of(value.peek());
|
|
return Optional.empty();
|
|
}
|
|
}
|
|
|
|
public T getValue() {
|
|
return valueIfPresent().orElseGet(() -> {
|
|
if (parent == null) throw new NoSuchElementException("No value present");
|
|
return parent.getValue();
|
|
});
|
|
}
|
|
|
|
public boolean isPresent() {
|
|
if (!value.isEmpty()) return true;
|
|
if (parent == null) return false;
|
|
return parent.isPresent();
|
|
}
|
|
|
|
public void setValue(T value) {
|
|
synchronized (this.value) {
|
|
this.value.push(value);
|
|
}
|
|
}
|
|
|
|
public void clear() {
|
|
synchronized (this.value) {
|
|
value.pop();
|
|
}
|
|
}
|
|
|
|
public T orElse(T other) {
|
|
return valueIfPresent().orElseGet(() -> {
|
|
if (parent == null) return other;
|
|
return parent.orElse(other);
|
|
});
|
|
}
|
|
|
|
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
|
|
Optional<T> v = valueIfPresent();
|
|
if (v.isPresent()) return v.get();
|
|
if (parent == null) throw exceptionSupplier.get();
|
|
return parent.orElseThrow(exceptionSupplier);
|
|
}
|
|
}
|
|
}
|