mirror of
https://github.com/PaperMC/Folia.git
synced 2024-11-22 12:05:12 +01:00
Add watchdog thread
When regions take too long, having the server print the stacktrace of the ticking region should help debug the cause.
This commit is contained in:
parent
d8461e8ec5
commit
2e88b1680f
199
patches/server/0019-Add-watchdog-thread.patch
Normal file
199
patches/server/0019-Add-watchdog-thread.patch
Normal file
@ -0,0 +1,199 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Fri, 30 Aug 2024 18:34:32 -0700
|
||||
Subject: [PATCH] Add watchdog thread
|
||||
|
||||
When regions take too long, having the server print the stacktrace
|
||||
of the ticking region should help debug the cause.
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/threadedregions/FoliaWatchdogThread.java b/src/main/java/io/papermc/paper/threadedregions/FoliaWatchdogThread.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..258d82ab2c78482e1561343e8e1f81fc33f1895e
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/threadedregions/FoliaWatchdogThread.java
|
||||
@@ -0,0 +1,104 @@
|
||||
+package io.papermc.paper.threadedregions;
|
||||
+
|
||||
+import com.mojang.logging.LogUtils;
|
||||
+import io.papermc.paper.threadedregions.TickRegionScheduler.RegionScheduleHandle;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import org.bukkit.Bukkit;
|
||||
+import org.slf4j.Logger;
|
||||
+import org.spigotmc.WatchdogThread;
|
||||
+import java.lang.management.ManagementFactory;
|
||||
+import java.util.ArrayList;
|
||||
+import java.util.LinkedHashSet;
|
||||
+import java.util.List;
|
||||
+import java.util.concurrent.TimeUnit;
|
||||
+
|
||||
+public final class FoliaWatchdogThread extends Thread {
|
||||
+
|
||||
+ private static final Logger LOGGER = LogUtils.getLogger();
|
||||
+
|
||||
+ public static final class RunningTick {
|
||||
+
|
||||
+ public final long start;
|
||||
+ public final RegionScheduleHandle handle;
|
||||
+ public final Thread thread;
|
||||
+
|
||||
+ private long lastPrint;
|
||||
+
|
||||
+ public RunningTick(final long start, final RegionScheduleHandle handle, final Thread thread) {
|
||||
+ this.start = start;
|
||||
+ this.handle = handle;
|
||||
+ this.thread = thread;
|
||||
+ this.lastPrint = start;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private final LinkedHashSet<RunningTick> ticks = new LinkedHashSet<>();
|
||||
+
|
||||
+ public FoliaWatchdogThread() {
|
||||
+ super("Folia Watchdog Thread");
|
||||
+ this.setDaemon(true);
|
||||
+ this.setUncaughtExceptionHandler((final Thread thread, final Throwable throwable) -> {
|
||||
+ LOGGER.error("Uncaught exception in thread '" + thread.getName() + "'", throwable);
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void run() {
|
||||
+ for (;;) {
|
||||
+ try {
|
||||
+ Thread.sleep(1000L);
|
||||
+ } catch (final InterruptedException ex) {}
|
||||
+
|
||||
+ if (MinecraftServer.getServer().hasStopped()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ final List<RunningTick> ticks;
|
||||
+ synchronized (this.ticks) {
|
||||
+ if (this.ticks.isEmpty()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ ticks = new ArrayList<>(this.ticks);
|
||||
+ }
|
||||
+
|
||||
+ final long now = System.nanoTime();
|
||||
+
|
||||
+ for (final RunningTick tick : ticks) {
|
||||
+ final long elapsed = now - tick.lastPrint;
|
||||
+ if (elapsed <= TimeUnit.SECONDS.toNanos(5L)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ tick.lastPrint = now;
|
||||
+
|
||||
+ final double totalElapsedS = (double)(now - tick.start) / 1.0E9;
|
||||
+
|
||||
+ if (tick.handle instanceof TickRegions.ConcreteRegionTickHandle region) {
|
||||
+ LOGGER.error(
|
||||
+ "Tick region located in world '" + region.region.world.getWorld().getName() + "' around chunk '"
|
||||
+ + region.region.region.getCenterChunk() + "' has not responded in " + totalElapsedS + "s:"
|
||||
+ );
|
||||
+ } else {
|
||||
+ // assume global
|
||||
+ LOGGER.error("Global region has not responded in " + totalElapsedS + "s:");
|
||||
+ }
|
||||
+
|
||||
+ WatchdogThread.dumpThread(
|
||||
+ ManagementFactory.getThreadMXBean().getThreadInfo(tick.thread.threadId(), Integer.MAX_VALUE),
|
||||
+ Bukkit.getServer().getLogger()
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public void addTick(final RunningTick tick) {
|
||||
+ synchronized (this.ticks) {
|
||||
+ this.ticks.add(tick);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public void removeTick(final RunningTick tick) {
|
||||
+ synchronized (this.ticks) {
|
||||
+ this.ticks.remove(tick);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/threadedregions/TickRegionScheduler.java b/src/main/java/io/papermc/paper/threadedregions/TickRegionScheduler.java
|
||||
index a18da3f3f245031f0547efe9b52a1f2a219ef04a..056fb1ca7b07d5e713dcbd951830b14fc9025f4c 100644
|
||||
--- a/src/main/java/io/papermc/paper/threadedregions/TickRegionScheduler.java
|
||||
+++ b/src/main/java/io/papermc/paper/threadedregions/TickRegionScheduler.java
|
||||
@@ -34,6 +34,13 @@ public final class TickRegionScheduler {
|
||||
public static final int TICK_RATE = 20;
|
||||
public static final long TIME_BETWEEN_TICKS = 1_000_000_000L / TICK_RATE; // ns
|
||||
|
||||
+ // Folia start - watchdog
|
||||
+ public static final FoliaWatchdogThread WATCHDOG_THREAD = new FoliaWatchdogThread();
|
||||
+ static {
|
||||
+ WATCHDOG_THREAD.start();
|
||||
+ }
|
||||
+ // Folia end - watchdog
|
||||
+
|
||||
private final SchedulerThreadPool scheduler;
|
||||
|
||||
public TickRegionScheduler(final int threads) {
|
||||
@@ -327,6 +334,8 @@ public final class TickRegionScheduler {
|
||||
}
|
||||
|
||||
final boolean ret;
|
||||
+ final FoliaWatchdogThread.RunningTick runningTick = new FoliaWatchdogThread.RunningTick(tickStart, this, Thread.currentThread()); // Folia - watchdog
|
||||
+ WATCHDOG_THREAD.addTick(runningTick); // Folia - watchdog
|
||||
try {
|
||||
ret = this.runRegionTasks(() -> {
|
||||
return !RegionScheduleHandle.this.cancelled.get() && canContinue.getAsBoolean();
|
||||
@@ -336,6 +345,7 @@ public final class TickRegionScheduler {
|
||||
// don't release region for another tick
|
||||
return null;
|
||||
} finally {
|
||||
+ WATCHDOG_THREAD.removeTick(runningTick); // Folia - watchdog
|
||||
final long tickEnd = System.nanoTime();
|
||||
final long cpuEnd = MEASURE_CPU_TIME ? THREAD_MX_BEAN.getCurrentThreadCpuTime() : 0L;
|
||||
|
||||
@@ -401,6 +411,8 @@ public final class TickRegionScheduler {
|
||||
this.currentTickingThread = Thread.currentThread();
|
||||
}
|
||||
|
||||
+ final FoliaWatchdogThread.RunningTick runningTick = new FoliaWatchdogThread.RunningTick(tickStart, this, Thread.currentThread()); // Folia - region threading
|
||||
+ WATCHDOG_THREAD.addTick(runningTick); // Folia - region threading
|
||||
try {
|
||||
// next start isn't updated until the end of this tick
|
||||
this.tickRegion(tickCount, tickStart, scheduledEnd);
|
||||
@@ -409,6 +421,7 @@ public final class TickRegionScheduler {
|
||||
// regionFailed will schedule a shutdown, so we should avoid letting this region tick further
|
||||
return false;
|
||||
} finally {
|
||||
+ WATCHDOG_THREAD.removeTick(runningTick); // Folia - region threading
|
||||
final long tickEnd = System.nanoTime();
|
||||
final long cpuEnd = MEASURE_CPU_TIME ? THREAD_MX_BEAN.getCurrentThreadCpuTime() : 0L;
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/threadedregions/TickRegions.java b/src/main/java/io/papermc/paper/threadedregions/TickRegions.java
|
||||
index b1c07e582dbf0a203cf734fdbcd8387a422af3a6..988fe74578065c9464f5639e5cc6af79619edef5 100644
|
||||
--- a/src/main/java/io/papermc/paper/threadedregions/TickRegions.java
|
||||
+++ b/src/main/java/io/papermc/paper/threadedregions/TickRegions.java
|
||||
@@ -330,9 +330,9 @@ public final class TickRegions implements ThreadedRegionizer.RegionCallbacks<Tic
|
||||
}
|
||||
}
|
||||
|
||||
- private static final class ConcreteRegionTickHandle extends TickRegionScheduler.RegionScheduleHandle {
|
||||
+ public static final class ConcreteRegionTickHandle extends TickRegionScheduler.RegionScheduleHandle { // Folia - watchdog
|
||||
|
||||
- private final TickRegionData region;
|
||||
+ public final TickRegionData region; // Folia - watchdog
|
||||
|
||||
private ConcreteRegionTickHandle(final TickRegionData region, final long start) {
|
||||
super(region, start);
|
||||
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
index f7a4fee9bb25ff256dc2e5ea26bfbceca6a49167..64e0acf28fc241b16a7bc8d3807062fd7758ed7f 100644
|
||||
--- a/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
@@ -245,7 +245,7 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre
|
||||
}
|
||||
}
|
||||
|
||||
- private static void dumpThread(ThreadInfo thread, Logger log)
|
||||
+ public static void dumpThread(ThreadInfo thread, Logger log) // Folia - watchdog
|
||||
{
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
//
|
Loading…
Reference in New Issue
Block a user