From 8917bf5b1b053ad5bcaa2822774f58a2d00026c4 Mon Sep 17 00:00:00 2001 From: Eric <79340750+ericd590@users.noreply.github.com> Date: Sat, 15 Nov 2025 19:04:21 +1100 Subject: [PATCH 1/2] fix function thread safety --- .../lang/function/ExprFunctionCall.java | 2 +- .../skript/lang/function/ScriptFunction.java | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/ExprFunctionCall.java b/src/main/java/ch/njol/skript/lang/function/ExprFunctionCall.java index 9ba3c82798e..7a19b8b96c3 100644 --- a/src/main/java/ch/njol/skript/lang/function/ExprFunctionCall.java +++ b/src/main/java/ch/njol/skript/lang/function/ExprFunctionCall.java @@ -23,7 +23,7 @@ public class ExprFunctionCall extends SimpleExpression implements KeyProvi private final FunctionReference function; private final Class[] returnTypes; private final Class returnType; - private final Map cache = new WeakHashMap<>(); + private final Map cache = Collections.synchronizedMap(new WeakHashMap<>()); public ExprFunctionCall(FunctionReference function) { this(function, function.returnTypes); diff --git a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java index 2ec28a76a45..7d52f1f075d 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -19,9 +19,9 @@ public class ScriptFunction extends Function implements ReturnHandler { private final Trigger trigger; - private boolean returnValueSet; - private T @Nullable [] returnValues; - private String @Nullable [] returnKeys; + private final ThreadLocal returnValueSet = ThreadLocal.withInitial(() -> false); + private final ThreadLocal returnValues = new ThreadLocal<>(); + private final ThreadLocal returnKeys = new ThreadLocal<>(); /** * @deprecated use {@link ScriptFunction#ScriptFunction(Signature, SectionNode)} instead. @@ -85,12 +85,12 @@ public ScriptFunction(Signature sign, SectionNode node) { trigger.execute(event); ClassInfo returnType = getReturnType(); - return returnType != null ? returnValues : null; + return returnType != null ? returnValues.get() : null; } @Override public @NotNull String @Nullable [] returnedKeys() { - return returnKeys; + return returnKeys.get(); } /** @@ -99,26 +99,26 @@ public ScriptFunction(Signature sign, SectionNode node) { @Deprecated(since = "2.9.0", forRemoval = true) @ApiStatus.Internal public final void setReturnValue(@Nullable T[] values) { - assert !returnValueSet; - returnValueSet = true; - this.returnValues = values; + assert !returnValueSet.get(); + returnValueSet.set(true); + this.returnValues.set(values); } @Override public boolean resetReturnValue() { - returnValueSet = false; - returnValues = null; - returnKeys = null; + returnValueSet.remove(); + returnValues.remove(); + returnKeys.remove(); return true; } @Override public final void returnValues(Event event, Expression value) { - assert !returnValueSet; - returnValueSet = true; - this.returnValues = value.getArray(event); + assert !returnValueSet.get(); + returnValueSet.set(true); + this.returnValues.set(value.getArray(event)); if (KeyProviderExpression.canReturnKeys(value)) - this.returnKeys = ((KeyProviderExpression) value).getArrayKeys(event); + this.returnKeys.set(((KeyProviderExpression) value).getArrayKeys(event)); } @Override From e2a14843b96781c940adf8475872cee2c66956f1 Mon Sep 17 00:00:00 2001 From: Eric <79340750+ericd590@users.noreply.github.com> Date: Sat, 15 Nov 2025 19:30:43 +1100 Subject: [PATCH 2/2] fix variable thread safety --- .../java/ch/njol/skript/lang/Variable.java | 2 +- .../ch/njol/skript/variables/Variables.java | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/Variable.java b/src/main/java/ch/njol/skript/lang/Variable.java index 9d41e9e2deb..cd3abd09355 100644 --- a/src/main/java/ch/njol/skript/lang/Variable.java +++ b/src/main/java/ch/njol/skript/lang/Variable.java @@ -69,7 +69,7 @@ public class Variable implements Expression, KeyReceiverExpression, Key private final boolean list; private final @Nullable Variable source; - private final Map cache = new WeakHashMap<>(); + private final Map cache = Collections.synchronizedMap(new WeakHashMap<>()); @SuppressWarnings("unchecked") private Variable(VariableString name, Class[] types, boolean local, boolean ephemeral, boolean list, @Nullable Variable source) { diff --git a/src/main/java/ch/njol/skript/variables/Variables.java b/src/main/java/ch/njol/skript/variables/Variables.java index 86532aa858a..9e0a36bc57f 100644 --- a/src/main/java/ch/njol/skript/variables/Variables.java +++ b/src/main/java/ch/njol/skript/variables/Variables.java @@ -457,23 +457,23 @@ public static Object getVariable(String name, @Nullable Event event, boolean loc return map.getVariable(n); } else { - // Prevent race conditions from returning variables with incorrect values - if (!changeQueue.isEmpty()) { - // Gets the last VariableChange made - VariableChange variableChange = changeQueue.stream() - .filter(change -> change.name.equals(n)) - .reduce((first, second) -> second) - // Gets last value, as iteration is from head to tail, - // and adding occurs at the tail (and we want the most recently added) - .orElse(null); - - if (variableChange != null) { - return variableChange.value; - } - } - try { variablesLock.readLock().lock(); + // Prevent race conditions from returning variables with incorrect values + if (!changeQueue.isEmpty()) { + // Gets the last VariableChange made + VariableChange variableChange = changeQueue.stream() + .filter(change -> change.name.equals(n)) + .reduce((first, second) -> second) + // Gets last value, as iteration is from head to tail, + // and adding occurs at the tail (and we want the most recently added) + .orElse(null); + + if (variableChange != null) { + return variableChange.value; + } + } + return variables.getVariable(n); } finally { variablesLock.readLock().unlock();