aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0666-Fix-GameProfileCache-concurrency.patch
blob: e95139c0f42c1e4545fa3de93e7b145057fac1e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 11 Jul 2020 05:09:28 -0700
Subject: [PATCH] Fix GameProfileCache concurrency

Separate lookup and state access locks prevent lookups
from stalling simple state access/write calls

diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java
index 4dc08b0abf0a1edb51cc586d1a89444ba790ca7f..13e05a9e42ba89d37c423d819da33f2f67fa9eac 100644
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
@@ -61,6 +61,11 @@ public class GameProfileCache {
     @Nullable
     private Executor executor;
 
+    // Paper start
+    protected final java.util.concurrent.locks.ReentrantLock stateLock = new java.util.concurrent.locks.ReentrantLock();
+    protected final java.util.concurrent.locks.ReentrantLock lookupLock = new java.util.concurrent.locks.ReentrantLock();
+    // Paper end
+
     public GameProfileCache(GameProfileRepository profileRepository, File cacheFile) {
         this.profileRepository = profileRepository;
         this.file = cacheFile;
@@ -68,11 +73,13 @@ public class GameProfileCache {
     }
 
     private void safeAdd(GameProfileCache.GameProfileInfo entry) {
+        try { this.stateLock.lock(); // Paper - allow better concurrency
         GameProfile gameprofile = entry.getProfile();
 
         entry.setLastAccess(this.getNextOperation());
         this.profilesByName.put(gameprofile.getName().toLowerCase(Locale.ROOT), entry);
         this.profilesByUUID.put(gameprofile.getId(), entry);
+        } finally { this.stateLock.unlock(); } // Paper - allow better concurrency
     }
 
     private static Optional<GameProfile> lookupGameProfile(GameProfileRepository repository, String name) {
@@ -129,17 +136,20 @@ public class GameProfileCache {
 
     // Paper start
     public @Nullable GameProfile getProfileIfCached(String name) {
+        try { this.stateLock.lock(); // Paper - allow better concurrency
         GameProfileCache.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT));
         if (entry == null) {
             return null;
         }
         entry.setLastAccess(this.getNextOperation());
         return entry.getProfile();
+        } finally { this.stateLock.unlock(); } // Paper - allow better concurrency
     }
     // Paper end
 
     public Optional<GameProfile> get(String name) {
         String s1 = name.toLowerCase(Locale.ROOT);
+        boolean stateLocked = true; try { this.stateLock.lock(); // Paper - allow better concurrency
         GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1);
         boolean flag = false;
 
@@ -155,8 +165,12 @@ public class GameProfileCache {
         if (usercache_usercacheentry != null) {
             usercache_usercacheentry.setLastAccess(this.getNextOperation());
             optional = Optional.of(usercache_usercacheentry.getProfile());
+            stateLocked = false; this.stateLock.unlock(); // Paper - allow better concurrency
         } else {
+            stateLocked = false; this.stateLock.unlock(); // Paper - allow better concurrency
+            try { this.lookupLock.lock(); // Paper - allow better concurrency
             optional = GameProfileCache.lookupGameProfile(this.profileRepository, name); // Spigot - use correct case for offline players
+            } finally { this.lookupLock.unlock(); } // Paper - allow better concurrency
             if (optional.isPresent()) {
                 this.add((GameProfile) optional.get());
                 flag = false;
@@ -168,6 +182,7 @@ public class GameProfileCache {
         }
 
         return optional;
+        } finally { if (stateLocked) {  this.stateLock.unlock(); } } // Paper - allow better concurrency
     }
 
     public CompletableFuture<Optional<GameProfile>> getAsync(String username) {
@@ -192,6 +207,7 @@ public class GameProfileCache {
     }
 
     public Optional<GameProfile> get(UUID uuid) {
+        try { this.stateLock.lock(); // Paper - allow better concurrency
         GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByUUID.get(uuid);
 
         if (usercache_usercacheentry == null) {
@@ -200,6 +216,7 @@ public class GameProfileCache {
             usercache_usercacheentry.setLastAccess(this.getNextOperation());
             return Optional.of(usercache_usercacheentry.getProfile());
         }
+        } finally { this.stateLock.unlock(); } // Paper - allow better concurrency
     }
 
     public void setExecutor(Executor executor) {
@@ -280,7 +297,7 @@ public class GameProfileCache {
         JsonArray jsonarray = new JsonArray();
         DateFormat dateformat = GameProfileCache.createDateFormat();
 
-        this.getTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach((usercache_usercacheentry) -> { // Spigot
+        this.listTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach((usercache_usercacheentry) -> { // Spigot // Paper - allow better concurrency
             jsonarray.add(GameProfileCache.writeGameProfile(usercache_usercacheentry, dateformat));
         });
         String s = this.gson.toJson(jsonarray);
@@ -321,8 +338,19 @@ public class GameProfileCache {
     }
 
     private Stream<GameProfileCache.GameProfileInfo> getTopMRUProfiles(int limit) {
-        return ImmutableList.copyOf(this.profilesByUUID.values()).stream().sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed()).limit((long) limit);
+        // Paper start - allow better concurrency
+        return this.listTopMRUProfiles(limit).stream();
+    }
+
+    private List<GameProfileCache.GameProfileInfo> listTopMRUProfiles(int limit) {
+        try {
+            this.stateLock.lock();
+            return this.profilesByUUID.values().stream().sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed()).limit(limit).toList();
+        } finally {
+            this.stateLock.unlock();
+        }
     }
+    // Paper end
 
     private static JsonElement writeGameProfile(GameProfileCache.GameProfileInfo entry, DateFormat dateFormat) {
         JsonObject jsonobject = new JsonObject();