diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java
index fe54ef7a..fb307910 100644
--- a/src/main/java/carpet/script/CarpetScriptHost.java
+++ b/src/main/java/carpet/script/CarpetScriptHost.java
@@ -267,10 +267,6 @@ public class CarpetScriptHost extends ScriptHost
@Override
public void onClose()
{
- super.onClose();
- if (this.saveTimeout > 0)
- dumpState();
-
FunctionValue closing = getFunction("__on_close");
if (closing != null)
{
@@ -307,7 +303,9 @@ public class CarpetScriptHost extends ScriptHost
e.remove();
}
}
-
+ if (this.saveTimeout > 0)
+ dumpState();
+ super.onClose();
}
private void dumpState()
diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java
index 0364945e..34c206c4 100644
--- a/src/main/java/carpet/script/CarpetScriptServer.java
+++ b/src/main/java/carpet/script/CarpetScriptServer.java
@@ -1,14 +1,15 @@
package carpet.script;
import carpet.script.bundled.BundledModule;
-import carpet.script.value.FunctionValue;
-import carpet.script.value.MapValue;
-import carpet.script.value.StringValue;
import carpet.CarpetSettings;
import carpet.CarpetServer;
import carpet.script.bundled.FileModule;
import carpet.script.bundled.Module;
import carpet.script.exception.InvalidCallbackException;
+import carpet.script.value.FunctionValue;
+import carpet.script.value.MapValue;
+import carpet.script.value.StringValue;
+import carpet.script.value.ThreadValue;
import carpet.script.value.Value;
import carpet.utils.Messenger;
import com.mojang.brigadier.arguments.StringArgumentType;
@@ -299,5 +300,6 @@ public class CarpetScriptServer
{
host.onClose();
}
+ ThreadValue.shutdown();
}
}
diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java
index b6be4cbf..eeed2959 100644
--- a/src/main/java/carpet/script/Expression.java
+++ b/src/main/java/carpet/script/Expression.java
@@ -2681,6 +2681,10 @@ public class Expression
*
task_completed(task)
* Returns true if task has completed, or false otherwise.
*
+ * synchronize(lock, expression)
+ * Evaluates expression synchronized with respect to the lock lock. Returns the value of the
+ * expression.
+ *
*
* Auxiliary functions
*
@@ -3118,6 +3122,23 @@ public class Expression
return new NumericValue(((ThreadValue) v).isFinished());
});
+ addLazyFunction("synchronize", -1, (c, t, lv) ->
+ {
+ if (lv.size() == 0) throw new InternalExpressionException("'synchronize' require at least an expression to synchronize");
+ Value lockValue = Value.NULL;
+ int ind = 0;
+ if (lv.size() == 2)
+ {
+ lockValue = lv.get(0).evalValue(c);
+ ind = 1;
+ }
+ synchronized (ThreadValue.getLock(lockValue))
+ {
+ Value ret = lv.get(ind).evalValue(c, t);
+ return (_c, _t) -> ret;
+ }
+ });
+
addLazyFunction("system_variable_get", -1, (c, t, lv) ->
{
if (lv.size() == 0) throw new InternalExpressionException("'system_variable_get' expects at least a key to be fetched");
diff --git a/src/main/java/carpet/script/value/ThreadValue.java b/src/main/java/carpet/script/value/ThreadValue.java
index 42682197..6d3998f6 100644
--- a/src/main/java/carpet/script/value/ThreadValue.java
+++ b/src/main/java/carpet/script/value/ThreadValue.java
@@ -10,9 +10,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
public class ThreadValue extends Value
{
@@ -20,6 +23,7 @@ public class ThreadValue extends Value
private long id;
private static long sequence = 0L;
private static final Map executorServices = new HashMap<>();
+ private static final Map locks = new ConcurrentHashMap<>();
public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx, List args)
{
@@ -100,4 +104,32 @@ public class ThreadValue extends Value
}
return 0;
}
+
+ public static Object getLock(Value name)
+ {
+ return locks.computeIfAbsent(name, (n) -> new Object());
+ }
+
+
+ public static void shutdown()
+ {
+ for (ExecutorService exec : executorServices.values())
+ {
+ exec.shutdown();
+ try
+ {
+ exec.awaitTermination(5000, TimeUnit.MILLISECONDS);
+ }
+ catch (InterruptedException ignored)
+ {
+ }
+ finally
+ {
+ exec.shutdownNow();
+ }
+ }
+ executorServices.clear();
+ locks.clear();
+ }
+
}