Skip to content

Commit 3ed71d1

Browse files
committed
fix: capture all possible failure states for proximity system
1 parent a66c4e3 commit 3ed71d1

6 files changed

Lines changed: 115 additions & 16 deletions

File tree

orebfuscator-core/src/main/java/dev/imprex/orebfuscator/OrebfuscatorDumpFile.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ private void initialize() {
5959
set("versions.orebfuscator", orebfuscator.orebfuscatorVersion().toString());
6060

6161
orebfuscator.systemMonitor().dump(createSection("system"));
62+
orebfuscator.executor().dump(createSection("executor"));
6263

6364
var statistics = createSection("statistics");
6465
orebfuscator.statisticsRegistry().entries().stream()

orebfuscator-core/src/main/java/dev/imprex/orebfuscator/obfuscation/DeobfuscationWorker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public void deobfuscate(WorldAccessor world, @Nullable List<BlockPos> blocks) {
7979
return;
8080
}
8181

82-
var timer = statistics.debofuscation.start();
82+
var timer = statistics.deobfuscation.start();
8383
try {
8484
this.deobfuscateNow(world, blocks);
8585
} finally {

orebfuscator-core/src/main/java/dev/imprex/orebfuscator/proximity/ProximitySystem.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public void start() {
4444
@Override
4545
public void run() {
4646
long processStart = System.nanoTime();
47-
process().whenComplete((v, throwable) -> {
47+
invokeProcess().whenComplete((v, throwable) -> {
4848
if (throwable != null) {
4949
OfcLogger.error("An error occurred while running proximity worker", throwable);
5050
}
@@ -65,11 +65,25 @@ public void run() {
6565
this.statistics.proximityWait.add(TimeUnit.MILLISECONDS.toNanos(waitMillis));
6666
this.executor.schedule(this, waitMillis, TimeUnit.MILLISECONDS);
6767
} else {
68-
this.executor.execute(this);
68+
// Schedule instead of executing directly to break reentrant execution chains of
69+
// OrebfuscatorExecutor and avoid potential StackOverflowError.
70+
this.executor.schedule(this, 0L, TimeUnit.NANOSECONDS);
71+
}
72+
}).whenComplete((v, throwable) -> {
73+
if (throwable != null) {
74+
OfcLogger.error("An error occurred while running proximity worker, can't recover!", throwable);
6975
}
7076
});
7177
}
7278

79+
private CompletableFuture<Void> invokeProcess() {
80+
try {
81+
return process();
82+
} catch (Exception e) {
83+
return CompletableFuture.failedFuture(e);
84+
}
85+
}
86+
7387
private CompletableFuture<Void> process() {
7488
var players = this.orebfuscator.players();
7589
if (players.isEmpty()) {

orebfuscator-core/src/main/java/dev/imprex/orebfuscator/statistics/ObfuscationStatistics.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
package dev.imprex.orebfuscator.statistics;
22

3-
import java.util.Map;
43
import java.util.StringJoiner;
54
import java.util.function.BiConsumer;
6-
import java.util.function.Consumer;
75
import dev.imprex.orebfuscator.util.RollingAverage;
86
import dev.imprex.orebfuscator.util.RollingTimer;
97

108
public class ObfuscationStatistics implements StatisticsSource {
119

12-
public final RollingTimer debofuscation = new RollingTimer(4096);
10+
public final RollingTimer deobfuscation = new RollingTimer(4096);
1311

1412
public final RollingTimer executorWaitTime = new RollingTimer(4096);
1513
public final RollingTimer executorUtilization = new RollingTimer(4096);
@@ -24,10 +22,10 @@ public class ObfuscationStatistics implements StatisticsSource {
2422

2523
@Override
2624
public void add(StringJoiner joiner) {
27-
long debofuscation = (long) this.debofuscation.average();
25+
long deobfuscation = (long) this.deobfuscation.average();
2826

29-
joiner.add(String.format(" - debofuscation: %s",
30-
time(debofuscation)));
27+
joiner.add(String.format(" - deobfuscation: %s",
28+
time(deobfuscation)));
3129

3230
long executorWaitTime = (long) this.executorWaitTime.average();
3331
double executorUtilization = this.executorUtilization.average();
@@ -66,13 +64,13 @@ public void add(StringJoiner joiner) {
6664

6765
@Override
6866
public void debug(BiConsumer<String, String> consumer) {
69-
consumer.accept("debofuscation", this.debofuscation.debugLong(this::time));
67+
consumer.accept("deobfuscation", this.deobfuscation.debugLong(this::time));
7068

7169
consumer.accept("executorWaitTime", this.executorWaitTime.debugLong(this::time));
7270
consumer.accept("executorUtilization", this.executorUtilization.debugDouble(this::percent));
7371

7472
consumer.accept("proximityWait", this.proximityWait.debugLong(this::time));
75-
consumer.accept("proximityProcess", this.proximityProcess.debugDouble(this::percent));
73+
consumer.accept("proximityProcess", this.proximityProcess.debugLong(this::time));
7674

7775
consumer.accept("missingNeighboringChunks", this.missingNeighboringChunks.debugDouble(this::faction));
7876

orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/concurrent/OrebfuscatorExecutor.java

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
package dev.imprex.orebfuscator.util.concurrent;
22

3+
import dev.imprex.orebfuscator.config.yaml.ConfigurationSection;
4+
import java.util.Arrays;
5+
import java.util.List;
36
import java.util.Objects;
7+
import java.util.concurrent.BlockingQueue;
48
import java.util.concurrent.Executor;
59
import java.util.concurrent.ExecutorService;
610
import java.util.concurrent.Executors;
11+
import java.util.concurrent.LinkedBlockingQueue;
712
import java.util.concurrent.ScheduledExecutorService;
813
import java.util.concurrent.ScheduledFuture;
14+
import java.util.concurrent.ScheduledThreadPoolExecutor;
15+
import java.util.concurrent.ThreadPoolExecutor;
916
import java.util.concurrent.TimeUnit;
1017
import java.util.concurrent.atomic.LongAdder;
18+
import java.util.stream.Collectors;
1119
import org.jspecify.annotations.NullMarked;
1220
import dev.imprex.orebfuscator.interop.OrebfuscatorCore;
1321
import dev.imprex.orebfuscator.statistics.OrebfuscatorStatistics;
1422

1523
@NullMarked
1624
public class OrebfuscatorExecutor implements Executor {
1725

18-
private final ScheduledExecutorService scheduledExecutorService = Executors
19-
.newSingleThreadScheduledExecutor(r -> new Thread(OrebfuscatorCore.THREAD_GROUP, r, "orebfuscator-scheduler"));
26+
private final ScheduledThreadPoolExecutor scheduledExecutorService = new ScheduledThreadPoolExecutor(
27+
1, r -> new Thread(OrebfuscatorCore.THREAD_GROUP, r, "orebfuscator-scheduler"));
2028

2129
private final int poolSize;
22-
private final ExecutorService executorService;
30+
private final ThreadPoolExecutor executorService;
2331

2432
private final LongAdder run = new LongAdder();
2533
private long updateTime = System.nanoTime();
@@ -30,7 +38,11 @@ public OrebfuscatorExecutor(OrebfuscatorCore orebfuscator) {
3038
this.statistics = orebfuscator.statistics();
3139

3240
this.poolSize = orebfuscator.config().advanced().threads();
33-
this.executorService = Executors.newFixedThreadPool(this.poolSize, OrebfuscatorThread::new);
41+
this.executorService = new ThreadPoolExecutor(
42+
this.poolSize, this.poolSize,
43+
0L, TimeUnit.MILLISECONDS,
44+
new LinkedBlockingQueue<>(),
45+
OrebfuscatorThread::new);
3446

3547
this.scheduledExecutorService.scheduleAtFixedRate(this::updateStatistics, 1L, 1L, TimeUnit.SECONDS);
3648
}
@@ -65,6 +77,79 @@ public void shutdown() {
6577
this.executorService.shutdownNow();
6678
}
6779

80+
public void dump(ConfigurationSection section) {
81+
dumpExecutor(section.createSection("scheduler"), this.scheduledExecutorService);
82+
dumpExecutor(section.createSection("core"), this.executorService);
83+
84+
ConfigurationSection threads = section.createSection("threads");
85+
86+
for (var entry : Thread.getAllStackTraces().entrySet()) {
87+
Thread thread = entry.getKey();
88+
89+
if (!(thread instanceof OrebfuscatorThread)) {
90+
continue;
91+
}
92+
93+
StackTraceElement[] stackTrace = entry.getValue();
94+
95+
ConfigurationSection threadSection =
96+
threads.createSection("thread-" + thread.getId());
97+
98+
threadSection.set("name", thread.getName());
99+
threadSection.set("id", thread.getId());
100+
threadSection.set("state", thread.getState().name());
101+
threadSection.set("alive", thread.isAlive());
102+
threadSection.set("daemon", thread.isDaemon());
103+
threadSection.set("priority", thread.getPriority());
104+
threadSection.set("stack", formatStackTrace(stackTrace));
105+
}
106+
}
107+
108+
private static void dumpExecutor(ConfigurationSection section, ThreadPoolExecutor executor) {
109+
section.set("shutdown", executor.isShutdown());
110+
section.set("terminating", executor.isTerminating());
111+
section.set("terminated", executor.isTerminated());
112+
113+
section.set("pool.size", executor.getPoolSize());
114+
section.set("pool.coreSize", executor.getCorePoolSize());
115+
section.set("pool.maxSize", executor.getMaximumPoolSize());
116+
section.set("pool.largestSize", executor.getLargestPoolSize());
117+
section.set("pool.active", executor.getActiveCount());
118+
119+
long taskCount = executor.getTaskCount();
120+
long completedTaskCount = executor.getCompletedTaskCount();
121+
122+
section.set("tasks.total", taskCount);
123+
section.set("tasks.completed", completedTaskCount);
124+
section.set("tasks.pending", Math.max(0, taskCount - completedTaskCount));
125+
126+
BlockingQueue<Runnable> queue = executor.getQueue();
127+
section.set("queue.type", queue.getClass().getName());
128+
section.set("queue.size", queue.size());
129+
section.set("queue.remainingCapacity", queue.remainingCapacity());
130+
131+
section.set("keepAliveMillis", executor.getKeepAliveTime(TimeUnit.MILLISECONDS));
132+
section.set("allowsCoreThreadTimeOut", executor.allowsCoreThreadTimeOut());
133+
134+
section.set("threadFactory", executor.getThreadFactory().getClass().getName());
135+
section.set("rejectedExecutionHandler", executor.getRejectedExecutionHandler().getClass().getName());
136+
137+
if (executor instanceof ScheduledThreadPoolExecutor scheduled) {
138+
section.set("scheduled.continueExistingPeriodicTasksAfterShutdown",
139+
scheduled.getContinueExistingPeriodicTasksAfterShutdownPolicy());
140+
section.set("scheduled.executeExistingDelayedTasksAfterShutdown",
141+
scheduled.getExecuteExistingDelayedTasksAfterShutdownPolicy());
142+
section.set("scheduled.removeOnCancel",
143+
scheduled.getRemoveOnCancelPolicy());
144+
}
145+
}
146+
147+
private static List<String> formatStackTrace(StackTraceElement[] stackTrace) {
148+
return Arrays.stream(stackTrace)
149+
.map(element -> "at " + element)
150+
.collect(Collectors.toList());
151+
}
152+
68153
private void updateStatistics() {
69154
long time = System.nanoTime();
70155
try {

orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitChunkPacketAccessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import net.imprex.orebfuscator.util.MinecraftVersion;
1515
import net.imprex.orebfuscator.util.WrappedClientboundLevelChunkPacketData;
1616
import org.jspecify.annotations.NullMarked;
17+
import org.jspecify.annotations.Nullable;
1718

1819
@NullMarked
1920
public class BukkitChunkPacketAccessor implements ChunkPacketAccessor {
@@ -29,7 +30,7 @@ public class BukkitChunkPacketAccessor implements ChunkPacketAccessor {
2930
private final byte[] data;
3031

3132
private final PacketContainer packet;
32-
private final WrappedClientboundLevelChunkPacketData packetData;
33+
private final @Nullable WrappedClientboundLevelChunkPacketData packetData;
3334

3435
public BukkitChunkPacketAccessor(PacketContainer packet, BukkitWorldAccessor worldAccessor) {
3536
this.packet = packet;

0 commit comments

Comments
 (0)