From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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 f45c658770..bdc98d12c6 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 extends IAsyncTaskHandlerReentrant<TickTas
         // Spigot start
         if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) {
             LOGGER.info("Saving usercache.json");
-            this.getUserCache().c();
+            this.getUserCache().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 65659996c3..d32bec70d0 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 {
 
     public static final SimpleDateFormat a = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
     private static boolean c;
-    private final Map<String, UserCache.UserCacheEntry> d = Maps.newHashMap();
-    private final Map<UUID, UserCache.UserCacheEntry> e = Maps.newHashMap();
+    private final Map<String, UserCache.UserCacheEntry> d = new java.util.concurrent.ConcurrentHashMap<>(); // Paper
+    private final Map<UUID, UserCache.UserCacheEntry> e = new java.util.concurrent.ConcurrentHashMap<>(); // Paper
     private final Deque<GameProfile> f = new java.util.concurrent.LinkedBlockingDeque<GameProfile>(); // CraftBukkit
     private final GameProfileRepository g;
     protected final Gson b;
@@ -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 {
 
         UserCache.UserCacheEntry usercache_usercacheentry = new UserCache.UserCacheEntry(gameprofile, date);
 
-        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 {
 
     }
 
+    // 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 = () -> {
+
         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
 
     }
 
--