threading

This commit is contained in:
gnembon committed 2019-12-16 08:56:22 -04:00
1 parent 8752ac7011
commit 5d32213922
7 files changed
+230 -44

No files matched your search

@@ -101,6 +101,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -969,27 +970,83 @@ public class CarpetExpression
if (sourceBlockState == targetBlockState && data == null)
return (c_, t_) -> Value.FALSE;
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
BlockState finalSourceBlockState = sourceBlockState;
BlockPos targetPos = targetLocator.block.getPos();
Clearable.clear(world.getBlockEntity(targetPos));
world.setBlockState(targetPos, sourceBlockState, 2);
if ( data != null)
cc.s.getMinecraftServer().executeSync( () ->
{
BlockEntity be = world.getBlockEntity(targetPos);
if (be != null)
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
Clearable.clear(world.getBlockEntity(targetPos));
world.setBlockState(targetPos, finalSourceBlockState, 2);
if (data != null)
{
CompoundTag destTag = data.method_10553();
destTag.putInt("x", targetPos.getX());
destTag.putInt("y", targetPos.getY());
destTag.putInt("z", targetPos.getZ());
be.fromTag(destTag);
be.markDirty();
BlockEntity be = world.getBlockEntity(targetPos);
if (be != null)
{
CompoundTag destTag = data.method_10553();
destTag.putInt("x", targetPos.getX());
destTag.putInt("y", targetPos.getY());
destTag.putInt("z", targetPos.getZ());
be.fromTag(destTag);
be.markDirty();
}
}
CarpetSettings.impendingFillSkipUpdates = false;
});
Value retval = new BlockValue(finalSourceBlockState, world, targetLocator.block.getPos());
return (c_, t_) -> retval;
});
this.expr.addLazyFunction("set_async", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
ServerWorld world = cc.s.getWorld();
if (lv.size() < 2 || lv.size() % 2 == 1)
throw new InternalExpressionException("'set' should have at least 2 params and odd attributes");
BlockValue.LocatorResult targetLocator = BlockValue.fromParams(cc, lv, 0);
BlockValue.LocatorResult sourceLocator = BlockValue.fromParams(cc, lv, targetLocator.offset, true);
BlockState sourceBlockState = sourceLocator.block.getBlockState();
BlockState targetBlockState = world.getBlockState(targetLocator.block.getPos());
if (sourceLocator.offset < lv.size())
{
StateFactory<Block, BlockState> states = sourceBlockState.getBlock().getStateFactory();
for (int i = sourceLocator.offset; i < lv.size(); i += 2)
{
String paramString = lv.get(i).evalValue(c).getString();
Property<?> property = states.getProperty(paramString);
if (property == null)
throw new InternalExpressionException("Property " + paramString + " doesn't apply to " + sourceLocator.block.getString());
String paramValue = lv.get(i + 1).evalValue(c).getString();
sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
}
}
CarpetSettings.impendingFillSkipUpdates = false;
Value retval = new BlockValue(sourceBlockState, world, targetLocator.block.getPos());
CompoundTag data = sourceLocator.block.getData();
if (sourceBlockState == targetBlockState && data == null)
return (c_, t_) -> Value.FALSE;
BlockState finalSourceBlockState = sourceBlockState;
BlockPos targetPos = targetLocator.block.getPos();
cc.s.getMinecraftServer().execute( () ->
{
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
Clearable.clear(world.getBlockEntity(targetPos));
world.setBlockState(targetPos, finalSourceBlockState, 2);
if (data != null)
{
BlockEntity be = world.getBlockEntity(targetPos);
if (be != null)
{
CompoundTag destTag = data.method_10553();
destTag.putInt("x", targetPos.getX());
destTag.putInt("y", targetPos.getY());
destTag.putInt("z", targetPos.getZ());
be.fromTag(destTag);
be.markDirty();
}
}
CarpetSettings.impendingFillSkipUpdates = false;
});
Value retval = new BlockValue(finalSourceBlockState, world, targetLocator.block.getPos());
return (c_, t_) -> retval;
});
@@ -2982,6 +3039,7 @@ public class CarpetExpression
this.expr.addLazyFunction("game_tick", -1, (c, t, lv) -> {
ServerCommandSource s = ((CarpetContext)c).s;
if (!s.getMinecraftServer().isOnThread()) throw new InternalExpressionException("Unable to run ticks from threads");
((MinecraftServerInterface)s.getMinecraftServer()).forceTick( () -> System.nanoTime()- CarpetServer.scriptServer.tickStart<50000000L);
if (lv.size()>0)
{
@@ -3064,12 +3122,19 @@ public class CarpetExpression
if (lv.size() <= locator.offset)
throw new InternalExpressionException("'plop' needs extra argument indicating what to plop");
String what = lv.get(locator.offset).evalValue(c).getString();
Boolean res = FeatureGenerator.spawn(what, ((CarpetContext)c).s.getWorld(), locator.block.getPos());
if (res == null)
return (c_, t_) -> Value.NULL;
if (what.equalsIgnoreCase("boulder")) // there might be more of those
this.forceChunkUpdate(locator.block.getPos(), ((CarpetContext)c).s.getWorld());
return (c_, t_) -> new NumericValue(res);
Value [] result = new Value[]{Value.NULL};
((CarpetContext)c).s.getMinecraftServer().executeSync( () ->
{
Boolean res = FeatureGenerator.spawn(what, ((CarpetContext) c).s.getWorld(), locator.block.getPos());
if (res == null)
return;
if (what.equalsIgnoreCase("boulder")) // there might be more of those
this.forceChunkUpdate(locator.block.getPos(), ((CarpetContext) c).s.getWorld());
result[0] = new NumericValue(res);
});
Value ret = result[0]; // preventing from lazy evaluating of the result in case a future completes later
return (_c, _t) -> ret;
});
this.expr.addLazyFunction("schedule", -1, (c, t, lv) -> {
@@ -38,9 +38,9 @@ public class CarpetScriptServer
//make static for now, but will change that later:
public CarpetScriptHost globalHost;
public Map<String, CarpetScriptHost> modules;
long tickStart;
public long tickStart;
public boolean stopAll;
Set<String> holyMoly;
private Set<String> holyMoly;
public CarpetEventServer events;
public static List<ModuleInterface> bundledModuleData = new ArrayList<ModuleInterface>(){{
+10 -10
View File
@@ -6,16 +6,16 @@ import java.util.Set;
public class Context
{
static final int NONE = 0;
static final int VOID = 1;
static final int BOOLEAN = 2;
static final int NUMBER = 3;
static final int STRING = 4;
static final int LIST = 5;
static final int ITERATOR = 6;
static final int SIGNATURE = 7;
static final int LOCALIZATION = 8;
static final int LVALUE = 9;
public static final int NONE = 0;
public static final int VOID = 1;
public static final int BOOLEAN = 2;
public static final int NUMBER = 3;
public static final int STRING = 4;
public static final int LIST = 5;
public static final int ITERATOR = 6;
public static final int SIGNATURE = 7;
public static final int LOCALIZATION = 8;
public static final int LVALUE = 9;
public Map<String, LazyValue> variables = new HashMap<>();
@@ -23,6 +23,7 @@ import carpet.script.value.ListValue;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.ThreadValue;
import carpet.script.value.Value;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.apache.commons.lang3.text.WordUtils;
@@ -2992,6 +2993,67 @@ public class Expression implements Cloneable
return (cc, tt) -> retval;
});
addLazyFunctionWithDelegation("task", -1, (c, t, expr, tok, lv) -> {
Value queue = Value.NULL;
Value funcDesc;
if (lv.size()==1)
{
funcDesc = lv.get(0).evalValue(c);
}
else if (lv.size() == 2)
{
queue = lv.get(0).evalValue(c);
funcDesc = lv.get(1).evalValue(c);
}
else
{
throw new InternalExpressionException("'task' takes one or two parameters");
}
FunctionValue fun;
if (!(funcDesc instanceof FunctionValue))
{
String name = funcDesc.getString();
funcDesc = c.host.globalFunctions.get(name);
if (funcDesc == null)
throw new InternalExpressionException("Function "+name+" is not defined yet");
}
fun = (FunctionValue)funcDesc;
if (fun.getArguments().size() > 0)
throw new InternalExpressionException("tasks can only accept functions that don't take any arguments");
ThreadValue thread = new ThreadValue(queue, fun, expr, tok, c);
Thread.yield();
return (cc, tt) -> thread;
});
addFunction("task_count", (lv) ->
{
if (lv.size() > 0)
{
return new NumericValue(ThreadValue.taskCount(lv.get(0)));
}
return new NumericValue(ThreadValue.taskCount());
});
addUnaryFunction("join_task", (v) -> {
if (!(v instanceof ThreadValue))
throw new InternalExpressionException("'join_task' could only be used with a task value");
return ((ThreadValue) v).join();
});
addUnaryFunction("stop_task", (v) -> {
if (!(v instanceof ThreadValue))
throw new InternalExpressionException("'join_task' could only be used with a task value");
((ThreadValue) v).stop();
return Value.NULL;
});
addUnaryFunction("is_task_completed", (v) -> {
if (!(v instanceof ThreadValue))
throw new InternalExpressionException("'join_task' could only be used with a task value");
return new NumericValue(((ThreadValue) v).isFinished());
});
}
static Expression none = new Expression("null");
@@ -246,7 +246,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
return items;
}
public Iterator<Value> iterator() { return new ArrayList<>(items).iterator(); }
public Iterator<Value> iterator() { return new ArrayList<>(items).iterator(); } // should be thread safe
public void extend(List<Value> subList)
{
@@ -1,29 +1,49 @@
package carpet.script.value;
import carpet.script.Context;
import carpet.script.LazyValue;
import carpet.script.Expression;
import carpet.script.Tokenizer;
import carpet.script.exception.InternalExpressionException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Supplier;
public class ThreadValue extends Value
{
CompletableFuture<Value> taskFuture;
private CompletableFuture<Value> taskFuture;
private long id;
private final Object lock;
private final boolean[] started;
private Supplier<Value>task;
private static long sequence = 0L;
private static final ExecutorService executorService = Executors.newCachedThreadPool();
private static final Map<Value,ThreadPoolExecutor> executorServices = new HashMap<>();
public ThreadValue(LazyValue expression, Context ctx)
public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx)
{
id = sequence++;
lock = new Object();
started = new boolean[]{false};
task = () ->
{
if (!started[0])
{
started[0] = true;
return function.lazyEval(ctx, Context.NONE, expr, token, Collections.emptyList()).evalValue(ctx);
}
return null;
};
taskFuture = CompletableFuture.supplyAsync(
() -> expression.evalValue(ctx),
executorService
() -> { synchronized (lock) { return task.get(); } },
executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool())
);
Thread.yield();
}
@Override
@@ -40,6 +60,18 @@ public class ThreadValue extends Value
public Value join()
{
if (!started[0])
{
synchronized (lock)
{
if (!started[0])
{
Value res = task.get();
taskFuture.complete(res);
return res;
}
}
}
try
{
return taskFuture.get();
@@ -57,7 +89,14 @@ public class ThreadValue extends Value
public void stop()
{
taskFuture.complete(Value.NULL);
synchronized (lock)
{
if (!taskFuture.isDone())
{
started[0] = true;
taskFuture.complete(Value.NULL);
}
}
}
@Override
@@ -72,7 +111,27 @@ public class ThreadValue extends Value
public int compareTo(Value o)
{
if (!(o instanceof ThreadValue))
throw new InternalExpressionException("Cannot compare ");
throw new InternalExpressionException("Cannot compare tasks to other types");
return (int) (this.id - ((ThreadValue) o).id);
}
@Override
public int hashCode()
{
return Long.hashCode(id);
}
public static int taskCount()
{
return executorServices.values().stream().map(ThreadPoolExecutor::getActiveCount).reduce(0, Integer::sum);
}
public static int taskCount(Value pool)
{
if (executorServices.containsKey(pool))
{
return executorServices.get(pool).getActiveCount();
}
return 0;
}
}
+1 -1
View File
@@ -110,7 +110,7 @@ public abstract class Value implements Comparable<Value>, Cloneable
@Override
public int compareTo(final Value o)
{
if (o instanceof NumericValue || o instanceof ListValue)
if (o instanceof NumericValue || o instanceof ListValue || o instanceof ThreadValue)
{
return -o.compareTo(this);
}