Merge branch 'master' into 1.17

This commit is contained in:
gnembon committed 2021-04-23 21:43:42 -03:00
commit 01387211c2
5 files changed
+51 -50

No files matched your search

+5 -2
View File
@@ -89,7 +89,7 @@ are not thread safe and require the execution to park on the server thread, wher
the off-tick time in between ticks that didn't take all 50ms. There are however benefits of running things in parallel,
like fine time control not relying on the tick clock, or running things independent on each other. You can still run
your actions on tick-by-tick basis, either taking control of the execution using `game_tick()` API function
(nasty solution), or scheduling tick using `schedule()` function (preffered solution), but threading gives much more control
(nasty solution), or scheduling tick using `schedule()` function (preferred solution), but threading gives much more control
on the timings without impacting the main game and is the only solution to solve problems in parallel
(see [scarpet camera](/src/main/resources/assets/carpet/scripts/camera.sc)).
@@ -103,7 +103,10 @@ effectively parallelized.
If the app is shutting down, creating new tasks via `task` will not succeed. Instead the new task will do nothing and return
`null`, so most threaded application should handle closing apps naturally. Keep in mind in case you rely on task return values,
that they will return `null` regardless of anything in these situations.
that they will return `null` regardless of anything in these situations. When app handles `__on_close()` event, new tasks cannot
be submitted at this point, but current tasks are not terminated. Apps can use that opportunity to gracefully shutdown their tasks.
Regardless if the app handles `__on_close()` event, or does anything with their tasks in it, all tasks will be terminated exceptionally
within the next 1.5 seconds.
### `task(function, ... args)`, `task_thread(executor, function, ... args)`
+9 -31
View File
@@ -148,11 +148,6 @@ public class Expression
{
try
{
if (v2 != null)
{
throw new ExpressionException(c, e, token, "Did not expect a second parameter for unary operator");
}
Value.assertNotNull(v);
return lazyfun.apply(c, t, v);
}
catch (RuntimeException exc)
@@ -174,7 +169,6 @@ public class Expression
{
try
{
Value.assertNotNull(v1, v2);
return lazyfun.apply(c, type, e, t, v1, v2);
}
catch (RuntimeException exc)
@@ -193,6 +187,7 @@ public class Expression
@Override
public LazyValue lazyEval(Context c, Integer type, Expression e, Tokenizer.Token t, List<LazyValue> lv)
{
ILazyFunction.checkInterrupts();
try
{
return lazyfun.apply(c, type, e, t, lv);
@@ -234,9 +229,9 @@ public class Expression
@Override
public LazyValue lazyEval(Context c, Integer t, Expression e, Tokenizer.Token token, LazyValue v1, LazyValue v2)
{
ILazyFunction.checkInterrupts();
try
{
Value.assertNotNull(v1, v2);
return lazyfun.apply(c, t, v1, v2);
}
catch (RuntimeException exc)
@@ -257,7 +252,6 @@ public class Expression
{
try
{
Value.assertNotNull(v1, v2); // does anybody needs those
Value ret = fun.apply(c, t, v1.evalValue(c, Context.NONE), v2.evalValue(c, Context.NONE));
return (cc, tt) -> ret;
}
@@ -291,7 +285,7 @@ public class Expression
@Override
public Value evalUnary(Value v1)
{
return fun.apply(Value.assertNotNull(v1));
return fun.apply(v1);
}
});
}
@@ -301,11 +295,7 @@ public class Expression
operators.put(surface, new AbstractOperator(precedence, leftAssoc)
{
@Override
public Value eval(Value v1, Value v2)
{
Value.assertNotNull(v1, v2);
return fun.apply(v1, v2);
}
public Value eval(Value v1, Value v2) { return fun.apply(v1, v2); }
});
}
@@ -315,10 +305,7 @@ public class Expression
functions.put(name, new AbstractFunction(1, name)
{
@Override
public Value eval(List<Value> parameters)
{
return fun.apply(Value.assertNotNull(parameters.get(0)));
}
public Value eval(List<Value> parameters) { return fun.apply(parameters.get(0)); }
});
}
@@ -327,13 +314,7 @@ public class Expression
functions.put(name, new AbstractFunction(2, name)
{
@Override
public Value eval(List<Value> parameters)
{
Value v1 = parameters.get(0);
Value v2 = parameters.get(1);
Value.assertNotNull(v1, v2);
return fun.apply(v1, v2);
}
public Value eval(List<Value> parameters) { return fun.apply(parameters.get(0), parameters.get(1)); }
});
}
@@ -342,12 +323,7 @@ public class Expression
functions.put(name, new AbstractFunction(-1, name)
{
@Override
public Value eval(List<Value> parameters)
{
for (Value v: parameters)
Value.assertNotNull(v);
return fun.apply(parameters);
}
public Value eval(List<Value> parameters) { return fun.apply(parameters); }
});
}
@@ -375,6 +351,7 @@ public class Expression
@Override
public LazyValue lazyEval(Context c, Integer i, Expression e, Tokenizer.Token t, List<LazyValue> lazyParams)
{
ILazyFunction.checkInterrupts();
try
{
return fun.apply(c, i, lazyParams);
@@ -394,6 +371,7 @@ public class Expression
@Override
public LazyValue lazyEval(Context c, Integer i, Expression e, Tokenizer.Token t, List<LazyValue> lazyParams)
{
ILazyFunction.checkInterrupts();
try
{
Value ret = fun.apply(c, i, unpackArgs(lazyParams, c, Context.NONE));
+8 -1
View File
@@ -37,6 +37,12 @@ public abstract class Fluff
boolean numParamsVaries();
LazyValue lazyEval(Context c, Integer type, Expression expr, Tokenizer.Token token, List<LazyValue> lazyParams);
static void checkInterrupts()
{
if (ScriptHost.mainThread != Thread.currentThread() && Thread.currentThread().isInterrupted())
throw new InternalExpressionException("Thread interrupted");
}
// lazy function has a chance to change execution based on contxt
}
@@ -130,6 +136,7 @@ public abstract class Fluff
private List<Value> params;
public Value evalValue(Context c, Integer type) {
ILazyFunction.checkInterrupts();
return AbstractFunction.this.eval(getParams(c));
}
@@ -210,7 +217,7 @@ public abstract class Fluff
{
throw new ExpressionException(cc, e, t, "Did not expect a second parameter for unary operator");
}
return (c, ignored_type) -> AbstractUnaryOperator.this.evalUnary(v1.evalValue(c));
return (c, ignored_type) -> AbstractUnaryOperator.this.evalUnary(v1.evalValue(c, Context.NONE));
}
catch (RuntimeException exc)
{
+29 -1
View File
@@ -19,8 +19,10 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -29,6 +31,7 @@ public abstract class ScriptHost
public static Map<Value, Value> systemGlobals = new ConcurrentHashMap<>();
private static final Map<Long, Random> randomizers = new Long2ObjectOpenHashMap<>();
public static Thread mainThread = null;
private final Map<Value, ThreadPoolExecutor> executorServices = new HashMap<>();
private final Map<Value, Object> locks = new ConcurrentHashMap<>();
protected boolean inTermination = false;
@@ -113,6 +116,7 @@ public abstract class ScriptHost
initializeModuleGlobals(moduleData);
this.moduleData.put(code, moduleData);
this.modules.put(code==null?null:code.getName(), code);
mainThread = Thread.currentThread();
}
void initializeModuleGlobals(ModuleData md)
@@ -375,8 +379,32 @@ public abstract class ScriptHost
public void onClose()
{
inTermination = true;
executorServices.values().forEach(ThreadPoolExecutor::shutdown);
for (ScriptHost uh : userHosts.values()) uh.onClose();
executorServices.values().forEach(e -> {e.shutdown(); e.shutdownNow();});
if (taskCount() > 0)
{
executorServices.values().forEach(e -> {
ExecutorService stopper = Executors.newSingleThreadExecutor();
stopper.submit( () -> {
try {
// Wait a while for existing tasks to terminate
if (!e.awaitTermination(1500, TimeUnit.MILLISECONDS)) {
e.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!e.awaitTermination(1500, TimeUnit.MILLISECONDS))
CarpetScriptServer.LOG.error("Failed to stop app's thread");
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
e.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
stopper.shutdown();
stopper.shutdownNow();
});
});
}
}
public void setPerPlayer(boolean isPerUser)
@@ -24,21 +24,6 @@ public abstract class Value implements Comparable<Value>, Cloneable
public String boundVariable;
public static <T> T assertNotNull(T t)
{
if (t == null)
throw new InternalExpressionException("Operand may not be null");
return t;
}
public static <T> void assertNotNull(T t1, T t2)
{
if (t1 == null)
throw new InternalExpressionException("First operand may not be null");
if (t2 == null)
throw new InternalExpressionException("Second operand may not be null");
}
public boolean isBound()
{
return boundVariable != null;