mirror of
https://hub.spigotmc.org/stash/scm/spigot/spigot.git
synced 2025-09-18 21:33:01 +00:00

* Now Tracks Commands * Resolved Performance issue with Bukkit HandlerList method. Now all timings simply uses the Spigot System * Performance Improvement simply not using TimedRegisteredListener too * Bug with SyncChunkLoad tracking resolved. Now properly tracks many aspects of sync chunk load. * Reset/On/Off accuracy - should no longer have any issues turning it on/off during runtime, so this has been re-enabled. * Paste command on RCON now works * Now tracks everything related to plugins too, so you can easily see total plugin cost * Now tracks Tasks better and where they came from * Now tracks plugins event handlers to the Listener/Method name too. * Merged some Bukkit Patches so all timings changes are in 1 patch. * Moved back to a CLQ for CustomTimingsHandler for thread safety for when tasks are created Async but then executed sync.
305 lines
11 KiB
Diff
305 lines
11 KiB
Diff
From 6c1df6dfd24fdea8537b12c807c3d947665a6f75 Mon Sep 17 00:00:00 2001
|
|
From: md_5 <md_5@live.com.au>
|
|
Date: Sat, 23 Feb 2013 12:33:20 +1100
|
|
Subject: [PATCH] Watchdog Thread.
|
|
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index bfc3b88..6438a6c 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -498,6 +498,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
|
|
this.a(crashreport);
|
|
} finally {
|
|
try {
|
|
+ org.spigotmc.WatchdogThread.doStop();
|
|
this.stop();
|
|
this.isStopped = true;
|
|
} catch (Throwable throwable1) {
|
|
@@ -698,6 +699,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
|
|
SpigotTimings.tickablesTimer.stopTiming(); // Spigot
|
|
|
|
this.methodProfiler.b();
|
|
+ org.spigotmc.WatchdogThread.tick(); // Spigot
|
|
SpigotTimings.serverTickTimer.stopTiming(); // Spigot
|
|
org.spigotmc.CustomTimingsHandler.tick(); // Spigot
|
|
}
|
|
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
|
|
new file mode 100644
|
|
index 0000000..429c258
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/spigotmc/RestartCommand.java
|
|
@@ -0,0 +1,124 @@
|
|
+package org.spigotmc;
|
|
+
|
|
+import java.io.File;
|
|
+import java.util.List;
|
|
+import net.minecraft.server.EntityPlayer;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+
|
|
+public class RestartCommand extends Command
|
|
+{
|
|
+
|
|
+ public RestartCommand(String name)
|
|
+ {
|
|
+ super( name );
|
|
+ this.description = "Restarts the server";
|
|
+ this.usageMessage = "/restart";
|
|
+ this.setPermission( "bukkit.command.restart" );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean execute(CommandSender sender, String currentAlias, String[] args)
|
|
+ {
|
|
+ if ( testPermission( sender ) )
|
|
+ {
|
|
+ MinecraftServer.getServer().processQueue.add( new Runnable()
|
|
+ {
|
|
+ @Override
|
|
+ public void run()
|
|
+ {
|
|
+ restart();
|
|
+ }
|
|
+ } );
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ public static void restart()
|
|
+ {
|
|
+ restart( new File( SpigotConfig.restartScript ) );
|
|
+ }
|
|
+
|
|
+ public static void restart(final File script)
|
|
+ {
|
|
+ AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us
|
|
+ try
|
|
+ {
|
|
+ if ( script.isFile() )
|
|
+ {
|
|
+ System.out.println( "Attempting to restart with " + SpigotConfig.restartScript );
|
|
+
|
|
+ // Disable Watchdog
|
|
+ WatchdogThread.doStop();
|
|
+
|
|
+ // Kick all players
|
|
+ for ( EntityPlayer p : (List< EntityPlayer>) MinecraftServer.getServer().getPlayerList().players )
|
|
+ {
|
|
+ p.playerConnection.disconnect(SpigotConfig.restartMessage);
|
|
+ }
|
|
+ // Give the socket a chance to send the packets
|
|
+ try
|
|
+ {
|
|
+ Thread.sleep( 100 );
|
|
+ } catch ( InterruptedException ex )
|
|
+ {
|
|
+ }
|
|
+ // Close the socket so we can rebind with the new process
|
|
+ MinecraftServer.getServer().getServerConnection().b();
|
|
+
|
|
+ // Give time for it to kick in
|
|
+ try
|
|
+ {
|
|
+ Thread.sleep( 100 );
|
|
+ } catch ( InterruptedException ex )
|
|
+ {
|
|
+ }
|
|
+
|
|
+ // Actually shutdown
|
|
+ try
|
|
+ {
|
|
+ MinecraftServer.getServer().stop();
|
|
+ } catch ( Throwable t )
|
|
+ {
|
|
+ }
|
|
+
|
|
+ // This will be done AFTER the server has completely halted
|
|
+ Thread shutdownHook = new Thread()
|
|
+ {
|
|
+ @Override
|
|
+ public void run()
|
|
+ {
|
|
+ try
|
|
+ {
|
|
+ String os = System.getProperty( "os.name" ).toLowerCase();
|
|
+ if ( os.contains( "win" ) )
|
|
+ {
|
|
+ Runtime.getRuntime().exec( "cmd /c start " + script.getPath() );
|
|
+ } else
|
|
+ {
|
|
+ Runtime.getRuntime().exec( new String[]
|
|
+ {
|
|
+ "sh", script.getPath()
|
|
+ } );
|
|
+ }
|
|
+ } catch ( Exception e )
|
|
+ {
|
|
+ e.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+
|
|
+ shutdownHook.setDaemon( true );
|
|
+ Runtime.getRuntime().addShutdownHook( shutdownHook );
|
|
+ } else
|
|
+ {
|
|
+ System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." );
|
|
+ }
|
|
+ System.exit( 0 );
|
|
+ } catch ( Exception ex )
|
|
+ {
|
|
+ ex.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
|
|
index efcd193..2b499fe 100644
|
|
--- a/src/main/java/org/spigotmc/SpigotConfig.java
|
|
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
|
|
@@ -178,4 +178,18 @@ public class SpigotConfig
|
|
outdatedClientMessage = transform( getString( "messages.outdated-client", outdatedClientMessage ) );
|
|
outdatedServerMessage = transform( getString( "messages.outdated-server", outdatedServerMessage ) );
|
|
}
|
|
+
|
|
+ public static int timeoutTime = 60;
|
|
+ public static boolean restartOnCrash = true;
|
|
+ public static String restartScript = "./start.sh";
|
|
+ public static String restartMessage;
|
|
+ private static void watchdog()
|
|
+ {
|
|
+ timeoutTime = getInt( "settings.timeout-time", timeoutTime );
|
|
+ restartOnCrash = getBoolean( "settings.restart-on-crash", restartOnCrash );
|
|
+ restartScript = getString( "settings.restart-script", restartScript );
|
|
+ restartMessage = transform( getString( "messages.restart", "Server is restarting" ) );
|
|
+ commands.put( "restart", new RestartCommand( "restart" ) );
|
|
+ WatchdogThread.doStart( timeoutTime, restartOnCrash );
|
|
+ }
|
|
}
|
|
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
|
new file mode 100644
|
|
index 0000000..de08ad6
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
|
@@ -0,0 +1,117 @@
|
|
+package org.spigotmc;
|
|
+
|
|
+import java.lang.management.ManagementFactory;
|
|
+import java.lang.management.MonitorInfo;
|
|
+import java.lang.management.ThreadInfo;
|
|
+import java.util.logging.Level;
|
|
+import java.util.logging.Logger;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.bukkit.Bukkit;
|
|
+
|
|
+public class WatchdogThread extends Thread
|
|
+{
|
|
+
|
|
+ private static WatchdogThread instance;
|
|
+ private final long timeoutTime;
|
|
+ private final boolean restart;
|
|
+ private volatile long lastTick;
|
|
+ private volatile boolean stopping;
|
|
+
|
|
+ private WatchdogThread(long timeoutTime, boolean restart)
|
|
+ {
|
|
+ super( "Spigot Watchdog Thread" );
|
|
+ this.timeoutTime = timeoutTime;
|
|
+ this.restart = restart;
|
|
+ }
|
|
+
|
|
+ public static void doStart(int timeoutTime, boolean restart)
|
|
+ {
|
|
+ if ( instance == null )
|
|
+ {
|
|
+ instance = new WatchdogThread( timeoutTime * 1000L, restart );
|
|
+ instance.start();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void tick()
|
|
+ {
|
|
+ instance.lastTick = System.currentTimeMillis();
|
|
+ }
|
|
+
|
|
+ public static void doStop()
|
|
+ {
|
|
+ if ( instance != null )
|
|
+ {
|
|
+ instance.stopping = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run()
|
|
+ {
|
|
+ while ( !stopping )
|
|
+ {
|
|
+ //
|
|
+ if ( lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime )
|
|
+ {
|
|
+ Logger log = Bukkit.getServer().getLogger();
|
|
+ log.log( Level.SEVERE, "The server has stopped responding!" );
|
|
+ log.log( Level.SEVERE, "Please report this to http://www.spigotmc.org/" );
|
|
+ log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" );
|
|
+ log.log( Level.SEVERE, "Spigot version: " + Bukkit.getServer().getVersion() );
|
|
+ //
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
|
+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" );
|
|
+ dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE ), log );
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
|
+ //
|
|
+ log.log( Level.SEVERE, "Entire Thread Dump:" );
|
|
+ ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads( true, true );
|
|
+ for ( ThreadInfo thread : threads )
|
|
+ {
|
|
+ dumpThread( thread, log );
|
|
+ }
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
|
+
|
|
+ if ( restart )
|
|
+ {
|
|
+ RestartCommand.restart();
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ try
|
|
+ {
|
|
+ sleep( 10000 );
|
|
+ } catch ( InterruptedException ex )
|
|
+ {
|
|
+ interrupt();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static void dumpThread(ThreadInfo thread, Logger log)
|
|
+ {
|
|
+ log.log( Level.SEVERE, "------------------------------" );
|
|
+ //
|
|
+ log.log( Level.SEVERE, "Current Thread: " + thread.getThreadName() );
|
|
+ log.log( Level.SEVERE, "\tPID: " + thread.getThreadId()
|
|
+ + " | Suspended: " + thread.isSuspended()
|
|
+ + " | Native: " + thread.isInNative()
|
|
+ + " | State: " + thread.getThreadState() );
|
|
+ if ( thread.getLockedMonitors().length != 0 )
|
|
+ {
|
|
+ log.log( Level.SEVERE, "\tThread is waiting on monitor(s):" );
|
|
+ for ( MonitorInfo monitor : thread.getLockedMonitors() )
|
|
+ {
|
|
+ log.log( Level.SEVERE, "\t\tLocked on:" + monitor.getLockedStackFrame() );
|
|
+ }
|
|
+ }
|
|
+ log.log( Level.SEVERE, "\tStack:" );
|
|
+ //
|
|
+ for ( StackTraceElement stack : thread.getStackTrace() )
|
|
+ {
|
|
+ log.log( Level.SEVERE, "\t\t" + stack );
|
|
+ }
|
|
+ }
|
|
+}
|
|
--
|
|
1.9.1
|
|
|