From 9ba573afe6b92a2aeacb9ad818a5bad640f0b6ed Mon Sep 17 00:00:00 2001 From: gnembon Date: Tue, 17 Dec 2019 10:28:14 -0400 Subject: [PATCH] threading testing --- ...nager_scarpetThreadingProtectionMixin.java | 39 +++++++++++++ .../java/carpet/script/CarpetExpression.java | 5 ++ .../java/carpet/script/value/ThreadValue.java | 55 ++++++++++++++----- src/main/resources/carpet.mixins.json | 1 + 4 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java diff --git a/src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java b/src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java new file mode 100644 index 00000000..897b53b7 --- /dev/null +++ b/src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java @@ -0,0 +1,39 @@ +package carpet.mixins; + +import carpet.script.exception.InternalExpressionException; +import carpet.script.value.ThreadValue; +import net.minecraft.server.world.ServerChunkManager; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkStatus; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +@Mixin(ServerChunkManager.class) +public class ServerChunkManager_scarpetThreadingProtectionMixin +{ + @Redirect(method = "getChunk(IILnet/minecraft/world/chunk/ChunkStatus;Z)Lnet/minecraft/world/chunk/Chunk;", at = @At( + value = "INVOKE", + target = "Ljava/util/concurrent/CompletableFuture;join()Ljava/lang/Object;", + ordinal = 0 + )) + private Object checkJoiningThreads(CompletableFuture completableFuture) + { + synchronized (ThreadValue.waitingTreads) + { + if (ThreadValue.waitingTreads.containsKey(Thread.currentThread())) + throw new InternalExpressionException("Cannot join/wait on world accessing thread"); + ThreadValue.waitingTreads.put(Thread.currentThread(), 1); + } + Chunk res = completableFuture.join(); + ThreadValue.waitingTreads.remove(Thread.currentThread()); + return res; + + } +} diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index e5a27ac0..8596f64b 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -944,14 +944,18 @@ public class CarpetExpression this.expr.addLazyFunction("set", -1, (c, t, lv) -> { + CarpetSettings.LOG.error("starting"); 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); + CarpetSettings.LOG.error("dafuq"); BlockState sourceBlockState = sourceLocator.block.getBlockState(); + CarpetSettings.LOG.error("finish dafuq"); BlockState targetBlockState = world.getBlockState(targetLocator.block.getPos()); + CarpetSettings.LOG.error("finish dafuq 2"); if (sourceLocator.offset < lv.size()) { StateFactory states = sourceBlockState.getBlock().getStateFactory(); @@ -965,6 +969,7 @@ public class CarpetExpression sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState); } } + CarpetSettings.LOG.error("finish properties got"); CompoundTag data = sourceLocator.block.getData(); diff --git a/src/main/java/carpet/script/value/ThreadValue.java b/src/main/java/carpet/script/value/ThreadValue.java index 382cd6b2..6c5409bd 100644 --- a/src/main/java/carpet/script/value/ThreadValue.java +++ b/src/main/java/carpet/script/value/ThreadValue.java @@ -1,5 +1,6 @@ package carpet.script.value; +import carpet.CarpetSettings; import carpet.script.Context; import carpet.script.Expression; import carpet.script.Tokenizer; @@ -9,6 +10,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; @@ -17,9 +19,13 @@ import java.util.function.Supplier; public class ThreadValue extends Value { private CompletableFuture taskFuture; + //public static ThreadLocal joining = ThreadLocal.withInitial(() -> false); + public final static Map waitingTreads = new ConcurrentHashMap<>(); + public static Map joinRequestedThreads = new ConcurrentHashMap<>(); private long id; private final Object lock; - private final boolean[] started; + //private final boolean[] started; + private Thread executingThread; private Suppliertask; private static long sequence = 0L; @@ -29,18 +35,20 @@ public class ThreadValue extends Value { id = sequence++; lock = new Object(); - started = new boolean[]{false}; + executingThread = null; + //started = new boolean[]{false}; task = () -> { - if (!started[0]) + if (executingThread == null) { - started[0] = true; + CarpetSettings.LOG.error("Setting the thread"); + executingThread = Thread.currentThread(); return function.lazyEval(ctx, Context.NONE, expr, token, Collections.emptyList()).evalValue(ctx); } return null; }; taskFuture = CompletableFuture.supplyAsync( - () -> { synchronized (lock) { return task.get(); } }, + () -> { synchronized (lock) { CarpetSettings.LOG.error("getting in future"); Value ret = task.get(); CarpetSettings.LOG.error("gotten in future"); return ret;} }, executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool()) ); Thread.yield(); @@ -60,26 +68,45 @@ public class ThreadValue extends Value public Value join() { - if (!started[0]) + CarpetSettings.LOG.error("in join "+id); + if (executingThread == null) { - synchronized (lock) + CarpetSettings.LOG.error("not started "+id); + Value res; + waitingTreads.put(Thread.currentThread(), 1); + try { - if (!started[0]) - { - Value res = task.get(); - taskFuture.complete(res); - return res; - } + CarpetSettings.LOG.error(" getting result "+id); + res = task.get(); + CarpetSettings.LOG.error(" result got "+id); } + finally + { + waitingTreads.remove(Thread.currentThread()); + } + taskFuture.complete(res); + return res; } try { + synchronized (waitingTreads) + { + if (waitingTreads.containsKey(executingThread)) + throw new InternalExpressionException("Cannot wait/join on world accessing thread"); + waitingTreads.put(executingThread, 1); + } + CarpetSettings.LOG.error("getting future "+id); return taskFuture.get(); } catch (InterruptedException | ExecutionException e) { return Value.NULL; } + finally + { + waitingTreads.remove(executingThread); + CarpetSettings.LOG.error(" future gotten "+id); + } } public boolean isFinished() @@ -93,7 +120,7 @@ public class ThreadValue extends Value { if (!taskFuture.isDone()) { - started[0] = true; + executingThread = Thread.currentThread(); taskFuture.complete(Value.NULL); } } diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 5ce2fe8d..90b4e437 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -143,6 +143,7 @@ "FallingBlockEntity_scarpetEventsMixin", "ChunkTicketManager_scarpetMixin", "ServerChunkManager_scarpetMixin", + "ServerChunkManager_scarpetThreadingProtectionMixin", "PistonBlock_movableTEMixin",