diff --git a/Spigot-API-Patches/Remove-deadlock-risk-in-firing-async-events.patch b/Spigot-API-Patches/Remove-deadlock-risk-in-firing-async-events.patch new file mode 100644 index 0000000000..9b948d78f1 --- /dev/null +++ b/Spigot-API-Patches/Remove-deadlock-risk-in-firing-async-events.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 9 Sep 2018 00:32:05 -0400 +Subject: [PATCH] Remove deadlock risk in firing async events + +The PluginManager incorrectly used synchronization on firing any event +that was marked as synchronous. + +This synchronized did not even protect any concurrency risk as +handlers were already thread safe in terms of mutations during event +dispatch. + +The way it was used, has commonly led to deadlocks on the server, +which results in a hard crash. + +This change removes the synchronize and adds some protection around enable/disable + +diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +index cb2b0b9cb..a7dd902fb 100644 +--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java ++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +@@ -0,0 +0,0 @@ public final class SimplePluginManager implements PluginManager { + * @param plugin Plugin to check + * @return true if the plugin is enabled, otherwise false + */ +- public boolean isPluginEnabled(Plugin plugin) { ++ public synchronized boolean isPluginEnabled(Plugin plugin) { // Paper - synchronize + if ((plugin != null) && (plugins.contains(plugin))) { + return plugin.isEnabled(); + } else { +@@ -0,0 +0,0 @@ public final class SimplePluginManager implements PluginManager { + } + } + +- public void enablePlugin(final Plugin plugin) { ++ public synchronized void enablePlugin(final Plugin plugin) { // Paper - synchronize + if (!plugin.isEnabled()) { + List pluginCommands = PluginCommandYamlParser.parse(plugin); + +@@ -0,0 +0,0 @@ public final class SimplePluginManager implements PluginManager { + disablePlugin(plugin, false); + } + +- public void disablePlugin(final Plugin plugin, boolean closeClassloader) { ++ public synchronized void disablePlugin(final Plugin plugin, boolean closeClassloader) { // Paper - synchronize + // Paper end - close Classloader on disable + if (plugin.isEnabled()) { + try { +@@ -0,0 +0,0 @@ public final class SimplePluginManager implements PluginManager { + defaultPerms.get(false).clear(); + } + } ++ private void fireEvent(Event event) { callEvent(event); } // Paper - support old method incase plugin uses reflection + + /** + * Calls an event with the given details. +@@ -0,0 +0,0 @@ public final class SimplePluginManager implements PluginManager { + * @param event Event details + */ + public void callEvent(Event event) { +- if (event.isAsynchronous()) { +- if (Thread.holdsLock(this)) { +- throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code."); +- } +- if (server.isPrimaryThread()) { +- throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from primary server thread."); +- } +- fireEvent(event); +- } else { +- synchronized (this) { +- fireEvent(event); +- } +- } +- } +- +- private void fireEvent(Event event) { ++ // Paper - replace callEvent by merging to below method + HandlerList handlers = event.getHandlers(); + RegisteredListener[] listeners = handlers.getRegisteredListeners(); + +-- \ No newline at end of file