13d1abf01e
Upstream has released updates that appears to apply and compile correctly. This update has only been PARTIALLY tested by PaperMC and as with ANY update, please do your own testing I've tested basic region file saving as well as our oversized chunks approach. Bukkit Changes: e167e549 Clarify MerchantInventory#getSelectedRecipe. 3a1d5b8f Apply default permissions by registration order. c64cc93f Make tags Keyed ec037ed7 Added a method to get a list of tags bfb6ef86 Introduce rotation methods to the Vector class fc727372 Remove draft API from FluidLevelChangeEvent CraftBukkit Changes: 6430d9c0 SPIGOT-4632: BlockState location is not fixed 14cd1688 Fix CraftInventoryMerchant#getSelectedRecipe if there is no active merchant recipe. c24abab7 Load custom permissions after default permissions. bc99dfe8 Make tags Keyed 6fce004f Added a method to get a list of tags Spigot Changes: e5e5c7c6 Allow Saving Large Chunks e8d3881c Rebuild patches
122 lines
5 KiB
Diff
122 lines
5 KiB
Diff
From 0c1023b2f663801604199610c741023df370b001 Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
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 d925423d..59c70b8a 100644
|
|
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
|
|
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
|
|
@@ -386,7 +386,7 @@ 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 {
|
|
@@ -394,7 +394,7 @@ 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<Command> pluginCommands = PluginCommandYamlParser.parse(plugin);
|
|
|
|
@@ -431,7 +431,7 @@ 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 {
|
|
@@ -491,6 +491,7 @@ 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.
|
|
@@ -500,22 +501,7 @@ 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();
|
|
|
|
diff --git a/src/test/java/org/bukkit/plugin/PluginManagerTest.java b/src/test/java/org/bukkit/plugin/PluginManagerTest.java
|
|
index 6b86128e..56308c0c 100644
|
|
--- a/src/test/java/org/bukkit/plugin/PluginManagerTest.java
|
|
+++ b/src/test/java/org/bukkit/plugin/PluginManagerTest.java
|
|
@@ -19,7 +19,7 @@ public class PluginManagerTest {
|
|
private static final PluginManager pm = TestServer.getInstance().getPluginManager();
|
|
|
|
private final MutableObject store = new MutableObject();
|
|
-
|
|
+/* // Paper start - remove unneeded test
|
|
@Test
|
|
public void testAsyncSameThread() {
|
|
final Event event = new TestEvent(true);
|
|
@@ -30,14 +30,14 @@ public class PluginManagerTest {
|
|
return;
|
|
}
|
|
throw new IllegalStateException("No exception thrown");
|
|
- }
|
|
+ }*/ // Paper end
|
|
|
|
@Test
|
|
public void testSyncSameThread() {
|
|
final Event event = new TestEvent(false);
|
|
pm.callEvent(event);
|
|
}
|
|
-
|
|
+/* // Paper start - remove unneeded test
|
|
@Test
|
|
public void testAsyncLocked() throws InterruptedException {
|
|
final Event event = new TestEvent(true);
|
|
@@ -58,7 +58,7 @@ public class PluginManagerTest {
|
|
secondThread.join();
|
|
assertThat(store.value, is(instanceOf(IllegalStateException.class)));
|
|
assertThat(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code.", is(((Throwable) store.value).getMessage()));
|
|
- }
|
|
+ }*/ // Paper end
|
|
|
|
@Test
|
|
public void testAsyncUnlocked() throws InterruptedException {
|
|
--
|
|
2.20.1
|
|
|