Merge branch 'master' into 1.17
This commit is contained in:
commit
01387211c2
5 files changed
+51
-50
No files matched your search
@@ -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)`
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user