From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Fri, 24 Apr 2020 09:06:15 -0700
Subject: [PATCH] Make CallbackExecutor strict again

The correct fix for double scheduling is to avoid it. The reason
this class is used is because double scheduling causes issues
elsewhere, and it acts as an explicit detector of what double
schedules. Effectively, use the callback executor as a tool of
finding issues rather than hiding these issues.

This patch also reverts incorrect use(s) of the class by paper.

- getChunkFutureAsynchronously
  There is no risk at all of recursion. The future is executed on
  the chunk provider's thread queue, the same place general plugin
  load callbacks are executed on. Forcing the task execution into
  the callback executor also prevents the future from catching
  any exception thrown from it.

diff --git a/src/main/java/net/minecraft/server/level/ChunkProviderServer.java b/src/main/java/net/minecraft/server/level/ChunkProviderServer.java
index ff883a306a138c2940d3de6ec46dfeb9d98e5b93..bc8114715f09496efa8aec8426c53542fd444c12 100644
--- a/src/main/java/net/minecraft/server/level/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/level/ChunkProviderServer.java
@@ -199,9 +199,9 @@ public class ChunkProviderServer extends IChunkProvider {
 
                 try {
                     if (onLoad != null) {
-                        playerChunkMap.callbackExecutor.execute(() -> {
+                        // Tuinity - revert incorrect use of callback executor
                             onLoad.accept(either == null ? null : either.left().orElse(null)); // indicate failure to the callback.
-                        });
+                        // Tuinity - revert incorrect use of callback executor
                     }
                 } catch (Throwable thr) {
                     if (thr instanceof ThreadDeath) {
diff --git a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
index 31d5356b1498f55ef53f3c66b6629bc556b9508f..2beb14192910a35936e0ff5878c227bf22abd442 100644
--- a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
@@ -176,31 +176,28 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
     // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
     public final CallbackExecutor callbackExecutor = new CallbackExecutor();
     public static final class CallbackExecutor implements java.util.concurrent.Executor, Runnable {
-
-        // Paper start - replace impl with recursive safe multi entry queue
-        // it's possible to schedule multiple tasks currently, so it's vital we change this impl
-        // If we recurse into the executor again, we will append to another queue, ensuring task order consistency
-        private java.util.ArrayDeque<Runnable> queued = new java.util.ArrayDeque<>();
+        // Tuinity start - revert paper's change
+        private Runnable queued;
 
         @Override
         public void execute(Runnable runnable) {
             AsyncCatcher.catchOp("Callback Executor execute");
-            if (queued == null) {
-                queued = new java.util.ArrayDeque<>();
+            if (queued != null) {
+                MinecraftServer.LOGGER.fatal("Failed to schedule runnable", new IllegalStateException("Already queued")); // Paper - make sure this is printed
+                throw new IllegalStateException("Already queued");
             }
-            queued.add(runnable);
+            queued = runnable;
         }
+        // Tuinity end - revert paper's change
 
         @Override
         public void run() {
             AsyncCatcher.catchOp("Callback Executor run");
-            if (queued == null) {
-                return;
-            }
-            java.util.ArrayDeque<Runnable> queue = queued;
+            // Tuinity start - revert paper's change
+            Runnable task = queued;
             queued = null;
-            Runnable task;
-            while ((task = queue.pollFirst()) != null) {
+            if (task != null) {
+                // Tuinity end - revert paper's change
                 task.run();
             }
         }
