899bc53b79
Upstream has released updates that appears to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: f47abd88 SPIGOT-6242: Fix some file line endings de96535b SPIGOT-6234: enum classes don't serialize properly when implementing ConfigurationSerializable CraftBukkit Changes: 4475707d SPIGOT-6244: /spawnpoint ignores angle 8b3b096d SPIGOT-6242: Fix some file line endings 4b33c749 SPIGOT-6186: Canceling a CreatureSpawnEvent results in a "Unable to summon entity due to duplicate UUIDs" error 2b3ca726 SPIGOT-6236: Vehicle passenger portal cooldown does not change
137 lines
6 KiB
Diff
137 lines
6 KiB
Diff
From 0000000000000000000000000000000000000000 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/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
|
|
index 5e988ac7a639476c0f80786c8aea51ba3377fd10..d6451c82e0189e2c591a0fc1f884e83931d5a41b 100644
|
|
--- a/src/main/java/org/bukkit/entity/Entity.java
|
|
+++ b/src/main/java/org/bukkit/entity/Entity.java
|
|
@@ -28,7 +28,7 @@ import org.jetbrains.annotations.Nullable;
|
|
*/
|
|
public interface Entity extends Metadatable, CommandSender, Nameable, PersistentDataHolder {
|
|
|
|
- /**
|
|
+ /*
|
|
* Gets the entity's current position
|
|
*
|
|
* @return a new copy of Location containing the position of this entity
|
|
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
|
|
index a7393d2830b95d7167121b02066a3f357cee6085..a1a805004941d67abb0b9aa1721e0370c45b5289 100644
|
|
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
|
|
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
|
|
@@ -462,7 +462,7 @@ public final class SimplePluginManager implements PluginManager {
|
|
* @return true if the plugin is enabled, otherwise false
|
|
*/
|
|
@Override
|
|
- public boolean isPluginEnabled(@Nullable Plugin plugin) {
|
|
+ public synchronized boolean isPluginEnabled(@Nullable Plugin plugin) { // Paper - synchronize
|
|
if ((plugin != null) && (plugins.contains(plugin))) {
|
|
return plugin.isEnabled();
|
|
} else {
|
|
@@ -471,7 +471,7 @@ public final class SimplePluginManager implements PluginManager {
|
|
}
|
|
|
|
@Override
|
|
- public void enablePlugin(@NotNull final Plugin plugin) {
|
|
+ public synchronized void enablePlugin(@NotNull final Plugin plugin) { // Paper - synchronize
|
|
if (!plugin.isEnabled()) {
|
|
List<Command> pluginCommands = PluginCommandYamlParser.parse(plugin);
|
|
|
|
@@ -509,7 +509,7 @@ public final class SimplePluginManager implements PluginManager {
|
|
}
|
|
|
|
@Override
|
|
- public void disablePlugin(@NotNull final Plugin plugin, boolean closeClassloader) {
|
|
+ public synchronized void disablePlugin(@NotNull final Plugin plugin, boolean closeClassloader) { // Paper - synchronize
|
|
// Paper end - close Classloader on disable
|
|
if (plugin.isEnabled()) {
|
|
try {
|
|
@@ -579,6 +579,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.
|
|
@@ -587,23 +588,13 @@ public final class SimplePluginManager implements PluginManager {
|
|
*/
|
|
@Override
|
|
public void callEvent(@NotNull 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.");
|
|
- }
|
|
- } else {
|
|
- if (!server.isPrimaryThread()) {
|
|
- throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from another thread.");
|
|
- }
|
|
+ // Paper - replace callEvent by merging to below method
|
|
+ if (event.isAsynchronous() && server.isPrimaryThread()) {
|
|
+ throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously.");
|
|
+ } else if (!event.isAsynchronous() && !server.isPrimaryThread()) {
|
|
+ throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
|
|
}
|
|
|
|
- fireEvent(event);
|
|
- }
|
|
-
|
|
- private void fireEvent(@NotNull Event event) {
|
|
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 f188cd4f3b07027c30d41f1162db77a506b7b6bb..1941c9f49e9514c1236c5f4ea9f7af47f7be85c5 100644
|
|
--- a/src/test/java/org/bukkit/plugin/PluginManagerTest.java
|
|
+++ b/src/test/java/org/bukkit/plugin/PluginManagerTest.java
|
|
@@ -17,7 +17,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);
|
|
@@ -28,14 +28,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);
|
|
@@ -129,7 +129,7 @@ public class PluginManagerTest {
|
|
if (store.value == null) {
|
|
throw new IllegalStateException("No exception thrown");
|
|
}
|
|
- }
|
|
+ } */ // Paper
|
|
|
|
@Test
|
|
public void testRemovePermissionByNameLower() {
|