From 730e3ceb1bd9f09fc974018c1dbc1637dc604337 Mon Sep 17 00:00:00 2001 From: CraftBukkit/Spigot Date: Sun, 7 Oct 2012 15:08:21 -0500 Subject: [PATCH] Queue tasks from secondary threads. Fixes BUKKIT-2546 and BUKKIT-2600 This change affects the old chat compatibility layer from an implementation only standpoint. It does not queue the 'event' to fire, but rather queues a runnable that allows the calling thread to wait for execution to finish. The other effect of this change is that rcon connects now have their commands queued to be run on next server tick using the same implementation. The internal implementation is in org.bukkit.craftbukkit.util.Waitable. It is very similar to a Future task, but only contains minimal implementation with object.wait() and object.notify() calls under the hood of waitable.get() and waitable.run(). PlayerPreLoginEvent now properly implements thread-safe event execution by queuing the events similar to chat and rcon. This is still a poor way albeit proper way to implement thread-safety; PlayerPreLoginEvent will stay deprecated. By: Wesley Wolfe --- .../org/bukkit/craftbukkit/util/Waitable.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 paper-server/src/main/java/org/bukkit/craftbukkit/util/Waitable.java diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/Waitable.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Waitable.java new file mode 100644 index 0000000000..5cd1154348 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/Waitable.java @@ -0,0 +1,46 @@ +package org.bukkit.craftbukkit.util; + +import java.util.concurrent.ExecutionException; + + +public abstract class Waitable implements Runnable { + private enum Status { + WAITING, + RUNNING, + FINISHED, + } + Throwable t = null; + T value = null; + Status status = Status.WAITING; + + public final void run() { + synchronized (this) { + if (status != Status.WAITING) { + throw new IllegalStateException("Invalid state " + status); + } + status = Status.RUNNING; + } + try { + value = evaluate(); + } catch (Throwable t) { + this.t = t; + } finally { + synchronized (this) { + status = Status.FINISHED; + this.notifyAll(); + } + } + } + + protected abstract T evaluate(); + + public synchronized T get() throws InterruptedException, ExecutionException { + while (status != Status.FINISHED) { + this.wait(); + } + if (t != null) { + throw new ExecutionException(t); + } + return value; + } +}