better thread handling and removal of unused code.

This commit is contained in:
gnembon committed 2021-04-23 21:42:55 -03:00
1 parent e22474156b
commit c476e814b4
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, 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 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 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 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)). (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 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, `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)` ### `task(function, ... args)`, `task_thread(executor, function, ... args)`
+9 -31
View File
@@ -148,11 +148,6 @@ public class Expression
{ {
try 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); return lazyfun.apply(c, t, v);
} }
catch (RuntimeException exc) catch (RuntimeException exc)
@@ -174,7 +169,6 @@ public class Expression
{ {
try try
{ {
Value.assertNotNull(v1, v2);
return lazyfun.apply(c, type, e, t, v1, v2); return lazyfun.apply(c, type, e, t, v1, v2);
} }
catch (RuntimeException exc) catch (RuntimeException exc)
@@ -193,6 +187,7 @@ public class Expression
@Override @Override
public LazyValue lazyEval(Context c, Integer type, Expression e, Tokenizer.Token t, List<LazyValue> lv) public LazyValue lazyEval(Context c, Integer type, Expression e, Tokenizer.Token t, List<LazyValue> lv)
{ {
ILazyFunction.checkInterrupts();
try try
{ {
return lazyfun.apply(c, type, e, t, lv); return lazyfun.apply(c, type, e, t, lv);
@@ -234,9 +229,9 @@ public class Expression
@Override @Override
public LazyValue lazyEval(Context c, Integer t, Expression e, Tokenizer.Token token, LazyValue v1, LazyValue v2) public LazyValue lazyEval(Context c, Integer t, Expression e, Tokenizer.Token token, LazyValue v1, LazyValue v2)
{ {
ILazyFunction.checkInterrupts();
try try
{ {
Value.assertNotNull(v1, v2);
return lazyfun.apply(c, t, v1, v2); return lazyfun.apply(c, t, v1, v2);
} }
catch (RuntimeException exc) catch (RuntimeException exc)
@@ -257,7 +252,6 @@ public class Expression
{ {
try 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)); Value ret = fun.apply(c, t, v1.evalValue(c, Context.NONE), v2.evalValue(c, Context.NONE));
return (cc, tt) -> ret; return (cc, tt) -> ret;
} }
@@ -291,7 +285,7 @@ public class Expression
@Override @Override
public Value evalUnary(Value v1) 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) operators.put(surface, new AbstractOperator(precedence, leftAssoc)
{ {
@Override @Override
public Value eval(Value v1, Value v2) public Value eval(Value v1, Value v2) { return fun.apply(v1, v2); }
{
Value.assertNotNull(v1, v2);
return fun.apply(v1, v2);
}
}); });
} }
@@ -315,10 +305,7 @@ public class Expression
functions.put(name, new AbstractFunction(1, name) functions.put(name, new AbstractFunction(1, name)
{ {
@Override @Override
public Value eval(List<Value> parameters) public Value eval(List<Value> parameters) { return fun.apply(parameters.get(0)); }
{
return fun.apply(Value.assertNotNull(parameters.get(0)));
}
}); });
} }
@@ -327,13 +314,7 @@ public class Expression
functions.put(name, new AbstractFunction(2, name) functions.put(name, new AbstractFunction(2, name)
{ {
@Override @Override
public Value eval(List<Value> parameters) public Value eval(List<Value> parameters) { return fun.apply(parameters.get(0), parameters.get(1)); }
{
Value v1 = parameters.get(0);
Value v2 = parameters.get(1);
Value.assertNotNull(v1, v2);
return fun.apply(v1, v2);
}
}); });
} }
@@ -342,12 +323,7 @@ public class Expression
functions.put(name, new AbstractFunction(-1, name) functions.put(name, new AbstractFunction(-1, name)
{ {
@Override @Override
public Value eval(List<Value> parameters) public Value eval(List<Value> parameters) { return fun.apply(parameters); }
{
for (Value v: parameters)
Value.assertNotNull(v);
return fun.apply(parameters);
}
}); });
} }
@@ -375,6 +351,7 @@ public class Expression
@Override @Override
public LazyValue lazyEval(Context c, Integer i, Expression e, Tokenizer.Token t, List<LazyValue> lazyParams) public LazyValue lazyEval(Context c, Integer i, Expression e, Tokenizer.Token t, List<LazyValue> lazyParams)
{ {
ILazyFunction.checkInterrupts();
try try
{ {
return fun.apply(c, i, lazyParams); return fun.apply(c, i, lazyParams);
@@ -394,6 +371,7 @@ public class Expression
@Override @Override
public LazyValue lazyEval(Context c, Integer i, Expression e, Tokenizer.Token t, List<LazyValue> lazyParams) public LazyValue lazyEval(Context c, Integer i, Expression e, Tokenizer.Token t, List<LazyValue> lazyParams)
{ {
ILazyFunction.checkInterrupts();
try try
{ {
Value ret = fun.apply(c, i, unpackArgs(lazyParams, c, Context.NONE)); 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(); boolean numParamsVaries();
LazyValue lazyEval(Context c, Integer type, Expression expr, Tokenizer.Token token, List<LazyValue> lazyParams); 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 // lazy function has a chance to change execution based on contxt
} }
@@ -130,6 +136,7 @@ public abstract class Fluff
private List<Value> params; private List<Value> params;
public Value evalValue(Context c, Integer type) { public Value evalValue(Context c, Integer type) {
ILazyFunction.checkInterrupts();
return AbstractFunction.this.eval(getParams(c)); 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"); 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) catch (RuntimeException exc)
{ {
+29 -1
View File
@@ -19,8 +19,10 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -29,6 +31,7 @@ public abstract class ScriptHost
public static Map<Value, Value> systemGlobals = new ConcurrentHashMap<>(); public static Map<Value, Value> systemGlobals = new ConcurrentHashMap<>();
private static final Map<Long, Random> randomizers = new Long2ObjectOpenHashMap<>(); 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, ThreadPoolExecutor> executorServices = new HashMap<>();
private final Map<Value, Object> locks = new ConcurrentHashMap<>(); private final Map<Value, Object> locks = new ConcurrentHashMap<>();
protected boolean inTermination = false; protected boolean inTermination = false;
@@ -113,6 +116,7 @@ public abstract class ScriptHost
initializeModuleGlobals(moduleData); initializeModuleGlobals(moduleData);
this.moduleData.put(code, moduleData); this.moduleData.put(code, moduleData);
this.modules.put(code==null?null:code.getName(), code); this.modules.put(code==null?null:code.getName(), code);
mainThread = Thread.currentThread();
} }
void initializeModuleGlobals(ModuleData md) void initializeModuleGlobals(ModuleData md)
@@ -375,8 +379,32 @@ public abstract class ScriptHost
public void onClose() public void onClose()
{ {
inTermination = true; inTermination = true;
executorServices.values().forEach(ThreadPoolExecutor::shutdown);
for (ScriptHost uh : userHosts.values()) uh.onClose(); 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) public void setPerPlayer(boolean isPerUser)
@@ -24,21 +24,6 @@ public abstract class Value implements Comparable<Value>, Cloneable
public String boundVariable; 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() public boolean isBound()
{ {
return boundVariable != null; return boundVariable != null;