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
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 8 Aug 2021 16:26:46 -0700
Subject: [PATCH] Do not submit profile lookups to worldgen threads
They block. On network I/O.
If enough tasks are submitted the server will eventually stall
out due to a sync load, as the worldgen threads will be
stalling on profile lookups.
diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
index c5fb6adb353538360ef420faee41565626eea1dc..33ec55ed1f7d10a5235330ad082e7c7b54c42368 100644
--- a/src/main/java/net/minecraft/Util.java
+++ b/src/main/java/net/minecraft/Util.java
@@ -81,6 +81,22 @@ public class Util {
private static final String MAX_THREADS_SYSTEM_PROPERTY = "max.bg.threads";
private static final AtomicInteger WORKER_COUNT = new AtomicInteger(1);
private static final ExecutorService BACKGROUND_EXECUTOR = makeExecutor("Main");
+ // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
+ public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() {
+
+ private final AtomicInteger count = new AtomicInteger();
+
+ @Override
+ public Thread newThread(Runnable run) {
+ Thread ret = new Thread(run);
+ ret.setName("Profile Lookup Executor #" + this.count.getAndIncrement());
+ ret.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> {
+ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
+ });
+ return ret;
+ }
+ });
+ // Paper end - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
private static final ExecutorService IO_POOL = makeIoExecutor();
private static final DateTimeFormatter FILENAME_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT);
public static TimeSource.NanoTimeSource timeSource = System::nanoTime;
diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java
index 58e923f4ef1980bc7fff1e3b3fcdaad8c4eded53..4038bb76339d43f18770624bd7fecc79b8d7f2a9 100644
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
@@ -181,7 +181,7 @@ public class GameProfileCache {
} else {
this.requests.put(username, CompletableFuture.supplyAsync(() -> {
return this.get(username);
- }, Util.backgroundExecutor()).whenCompleteAsync((optional, throwable) -> {
+ }, Util.PROFILE_EXECUTOR).whenCompleteAsync((optional, throwable) -> { // Paper - not a good idea to use BLOCKING OPERATIONS on the worldgen executor
this.requests.remove(username);
}, this.executor).whenCompleteAsync((optional, throwable) -> {
consumer.accept(optional);
diff --git a/src/main/java/net/minecraft/world/level/block/entity/SkullBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SkullBlockEntity.java
index 24ed280a31a10c822cb8b2d2e9bf43ad81d92924..a2fc2c0437999dd09f080eafe8ea466b16cdf57b 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/SkullBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/SkullBlockEntity.java
@@ -137,7 +137,7 @@ public class SkullBlockEntity extends BlockEntity {
public static void updateGameprofile(@Nullable GameProfile owner, Consumer<GameProfile> callback) {
if (owner != null && !StringUtil.isNullOrEmpty(owner.getName()) && (!owner.isComplete() || !owner.getProperties().containsKey("textures")) && profileCache != null && sessionService != null) {
profileCache.getAsync(owner.getName(), (profile) -> {
- Util.backgroundExecutor().execute(() -> {
+ Util.PROFILE_EXECUTOR.execute(() -> { // Paper - not a good idea to use BLOCKING OPERATIONS on the worldgen executor
Util.ifElse(profile, (profilex) -> {
Property property = Iterables.getFirst(profilex.getProperties().get("textures"), (Property)null);
if (property == null) {
diff --git a/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java b/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java
index 3030c153725415802f68c144e0b577d919307058..3e40d47f504248cd7caeef6b841a8aa6f1976170 100644
--- a/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java
+++ b/src/main/java/org/bukkit/craftbukkit/profile/CraftPlayerProfile.java
@@ -121,7 +121,7 @@ public final class CraftPlayerProfile implements PlayerProfile {
@Override
public CompletableFuture<PlayerProfile> update() {
- return CompletableFuture.supplyAsync(this::getUpdatedProfile, Util.backgroundExecutor());
+ return CompletableFuture.supplyAsync(this::getUpdatedProfile, Util.PROFILE_EXECUTOR); // Paper - not a good idea to use BLOCKING OPERATIONS on the worldgen executor
}
private CraftPlayerProfile getUpdatedProfile() {
|