PaperMC/patches/server/0673-Fix-GameProfileCache-concurrency.patch
Jake Potrebic 90fe0d58a5
Updated Upstream (Bukkit/CraftBukkit/Spigot) (#9825)
Upstream has released updates that appear 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:
897a0a23 SPIGOT-5753: Back PotionType by a minecraft registry
255b2aa1 SPIGOT-7080: Add World#locateNearestBiome
ff984826 Remove javadoc.io doc links

CraftBukkit Changes:
71b0135cc SPIGOT-5753: Back PotionType by a minecraft registry
a6bcb8489 SPIGOT-7080: Add World#locateNearestBiome
ad0e57434 SPIGOT-7502: CraftMetaItem - cannot deserialize BlockStateTag
b3efca57a SPIGOT-6400: Use Mockito instead of InvocationHandler
38c599f9d PR-1272: Only allow one entity in CraftItem instead of two
f065271ac SPIGOT-7498: ChunkSnapshot.getBlockEmittedLight() gets 64 blocks upper in Overworld

Spigot Changes:
e0e223fe Remove javadoc.io doc links
2023-10-22 20:12:00 +01:00

126 lines
6.3 KiB
Diff

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 eac9658fa4cab7a651e10e4e18c679e040e4aed0..f4d903ed4342c3100c30deda291dfd6dd4e1ef40 100644
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
@@ -60,6 +60,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;
@@ -67,11 +72,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) {
@@ -126,17 +133,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;
@@ -152,8 +162,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;
@@ -165,6 +179,7 @@ public class GameProfileCache {
}
return optional;
+ } finally { if (stateLocked) { this.stateLock.unlock(); } } // Paper - allow better concurrency
}
public CompletableFuture<Optional<GameProfile>> getAsync(String username) {
@@ -189,6 +204,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) {
@@ -197,6 +213,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) {
@@ -277,7 +294,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);
@@ -318,8 +335,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();