diff --git a/Spigot-Server-Patches/Optimize-UserCache-Thread-Safe.patch b/Spigot-Server-Patches/Optimize-UserCache-Thread-Safe.patch new file mode 100644 index 000000000..f7b0f3821 --- /dev/null +++ b/Spigot-Server-Patches/Optimize-UserCache-Thread-Safe.patch @@ -0,0 +1,98 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Mon, 16 May 2016 20:47:41 -0400 +Subject: [PATCH] Optimize UserCache / Thread Safe + +Because Techable keeps complaining about how this isn't thread safe, +easier to do this than replace the entire thing. + +Additionally, move Saving of the User cache to be done async, incase +the user never changed the default setting for Spigot's save on stop only. + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs + // Spigot start + if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { + LOGGER.info("Saving usercache.json"); +- this.X.c(); ++ this.X.c(false); // Paper + } + // Spigot end + } +diff --git a/src/main/java/net/minecraft/server/UserCache.java b/src/main/java/net/minecraft/server/UserCache.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/server/UserCache.java ++++ b/src/main/java/net/minecraft/server/UserCache.java +@@ -0,0 +0,0 @@ public class UserCache { + this.a(gameprofile, (Date) null); + } + +- private void a(GameProfile gameprofile, Date date) { ++ private synchronized void a(GameProfile gameprofile, Date date) { // Paper - synchronize + UUID uuid = gameprofile.getId(); + + if (date == null) { +@@ -0,0 +0,0 @@ public class UserCache { + String s = gameprofile.getName().toLowerCase(Locale.ROOT); + UserCache.UserCacheEntry usercache_usercacheentry = new UserCache.UserCacheEntry(gameprofile, date, null); + +- if (this.e.containsKey(uuid)) { ++ //if (this.e.containsKey(uuid)) { // Paper + UserCache.UserCacheEntry usercache_usercacheentry1 = (UserCache.UserCacheEntry) this.e.get(uuid); ++ if (usercache_usercacheentry1 != null) { // Paper + + this.d.remove(usercache_usercacheentry1.a().getName().toLowerCase(Locale.ROOT)); + this.f.remove(gameprofile); +@@ -0,0 +0,0 @@ public class UserCache { + } + + @Nullable +- public GameProfile getProfile(String s) { ++ public synchronized GameProfile getProfile(String s) { // Paper - synchronize + String s1 = s.toLowerCase(Locale.ROOT); + UserCache.UserCacheEntry usercache_usercacheentry = (UserCache.UserCacheEntry) this.d.get(s1); + +@@ -0,0 +0,0 @@ public class UserCache { + return usercache_usercacheentry == null ? null : usercache_usercacheentry.a(); + } + +- public String[] a() { ++ public synchronized String[] a() { // Paper - synchronize + ArrayList arraylist = Lists.newArrayList(this.d.keySet()); + + return (String[]) arraylist.toArray(new String[arraylist.size()]); +@@ -0,0 +0,0 @@ public class UserCache { + + } + ++ // Paper start + public void c() { ++ c(true); ++ } ++ public void c(boolean asyncSave) { ++ // Paper end + String s = this.b.toJson(this.a(org.spigotmc.SpigotConfig.userCacheCap)); ++ Runnable save = () -> { synchronized (this.h) { // Paper - ensure only 1 file is writing at same time by syncing to the FD ++ + BufferedWriter bufferedwriter = null; + + try { +@@ -0,0 +0,0 @@ public class UserCache { + } finally { + IOUtils.closeQuietly(bufferedwriter); + } ++ // Paper start ++ }}; ++ if (asyncSave) { ++ MCUtil.scheduleAsyncTask(save); ++ } else { ++ save.run(); ++ } ++ // Paper end + + } + +-- \ No newline at end of file