feat(commons): Implement fake ScopedValue for use until the real thing arrives
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2024-03-09 13:38:28 +01:00
parent fea0d4cc66
commit 3d3c657497
Signed by: Johannes
GPG Key ID: E76429612C2929F4
2 changed files with 132 additions and 0 deletions

View File

@ -0,0 +1,122 @@
package io.gitlab.jfronny.commons.concurrent;
import java.util.Deque;
import java.util.LinkedList;
import java.util.NoSuchElementException;
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 Deque<T> value;
private Carrier<T> parent;
public Carrier() {
this(null);
}
public Carrier(Carrier<T> parent) {
this.value = new LinkedList<>();
this.parent = parent;
}
public T getValue() {
if (!value.isEmpty()) return value.peek();
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) {
this.value.push(value);
}
public void clear() {
value.pop();
}
public T orElse(T other) {
if (!value.isEmpty()) return value.peek();
if (parent == null) return other;
return parent.orElse(other);
}
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (!value.isEmpty()) return value.peek();
if (parent == null) throw exceptionSupplier.get();
return parent.orElseThrow(exceptionSupplier);
}
}
}

View File

@ -0,0 +1,10 @@
package io.gitlab.jfronny.commons.concurrent;
public class StructureViolationException extends RuntimeException {
public StructureViolationException() {
}
public StructureViolationException(String message) {
super(message);
}
}