From 3d3c657497e63aab0b9c88925fd227d3cdcf183a Mon Sep 17 00:00:00 2001 From: JFronny Date: Sat, 9 Mar 2024 13:38:28 +0100 Subject: [PATCH] feat(commons): Implement fake ScopedValue for use until the real thing arrives --- .../commons/concurrent/ScopedValue.java | 122 ++++++++++++++++++ .../StructureViolationException.java | 10 ++ 2 files changed, 132 insertions(+) create mode 100644 commons/src/main/java/io/gitlab/jfronny/commons/concurrent/ScopedValue.java create mode 100644 commons/src/main/java/io/gitlab/jfronny/commons/concurrent/StructureViolationException.java diff --git a/commons/src/main/java/io/gitlab/jfronny/commons/concurrent/ScopedValue.java b/commons/src/main/java/io/gitlab/jfronny/commons/concurrent/ScopedValue.java new file mode 100644 index 0000000..83928e5 --- /dev/null +++ b/commons/src/main/java/io/gitlab/jfronny/commons/concurrent/ScopedValue.java @@ -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 The type of the value + */ +public class ScopedValue { + public static ScopedValue newInstance() { + return new ScopedValue<>(); + } + + public static R callWhere(ScopedValue key, T value, Callable op) throws Exception { + key.value.get().setValue(value); + try { + return op.call(); + } finally { + key.value.get().clear(); + } + } + + public static R getWhere(ScopedValue key, T value, Supplier op) { + key.value.get().setValue(value); + try { + return op.get(); + } finally { + key.value.get().clear(); + } + } + + public static void runWhere(ScopedValue key, T value, Runnable op) { + key.value.get().setValue(value); + try { + op.run(); + } finally { + key.value.get().clear(); + } + } + + private final InheritableThreadLocal> value = new InheritableThreadLocal<>() { + @Override + protected Carrier childValue(Carrier parentValue) { + return new Carrier<>(parentValue); + } + + @Override + protected Carrier 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 T orElseThrow(Supplier exceptionSupplier) throws X { + return value.get().orElseThrow(exceptionSupplier); + } + + private static class Carrier { + private Deque value; + private Carrier parent; + + public Carrier() { + this(null); + } + + public Carrier(Carrier 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 T orElseThrow(Supplier exceptionSupplier) throws X { + if (!value.isEmpty()) return value.peek(); + if (parent == null) throw exceptionSupplier.get(); + return parent.orElseThrow(exceptionSupplier); + } + } +} diff --git a/commons/src/main/java/io/gitlab/jfronny/commons/concurrent/StructureViolationException.java b/commons/src/main/java/io/gitlab/jfronny/commons/concurrent/StructureViolationException.java new file mode 100644 index 0000000..2c7bfca --- /dev/null +++ b/commons/src/main/java/io/gitlab/jfronny/commons/concurrent/StructureViolationException.java @@ -0,0 +1,10 @@ +package io.gitlab.jfronny.commons.concurrent; + +public class StructureViolationException extends RuntimeException { + public StructureViolationException() { + } + + public StructureViolationException(String message) { + super(message); + } +}