From a0db04c85894d318570b138a23fac0c4284dc807 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 7 Jun 2017 11:04:41 -0400 Subject: [PATCH] Shutdowns are only considered graceful if the server sends a killme ping (#25) --- TGServerService/DreamDaemon.cs | 44 +++++++++++++++++++++++++++++----- TGServerService/Interop.cs | 5 ++-- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/TGServerService/DreamDaemon.cs b/TGServerService/DreamDaemon.cs index fd61c43571..e07a352397 100644 --- a/TGServerService/DreamDaemon.cs +++ b/TGServerService/DreamDaemon.cs @@ -10,6 +10,13 @@ namespace TGServerService //It's not possible to actually click it while starting it in CL mode, so in order to change visibility etc. It restarts the process when the round ends partial class TGStationServer : ITGDreamDaemon { + enum ShutdownRequestPhase + { + None, + Requested, + Pinged, + } + const int DDHangStartTime = 60; const int DDBadStartTime = 10; @@ -22,11 +29,12 @@ namespace TGServerService object restartLock = new object(); bool RestartInProgress = false; - bool AwaitingShutdown = false; TGDreamDaemonSecurity StartingSecurity; TGDreamDaemonVisibility StartingVisiblity; + ShutdownRequestPhase AwaitingShutdown; + //Only need 1 proc instance void InitDreamDaemon() { @@ -77,9 +85,9 @@ namespace TGServerService { lock (watchdogLock) { - if (currentStatus != TGDreamDaemonStatus.Online) + if (currentStatus != TGDreamDaemonStatus.Online || AwaitingShutdown != ShutdownRequestPhase.None) return; - AwaitingShutdown = true; + AwaitingShutdown = ShutdownRequestPhase.Pinged; } SendCommand(SCGracefulShutdown); } @@ -113,6 +121,23 @@ namespace TGServerService } } + //handle a kill request from the server + public void KillMe() + { + bool DoRestart; + lock (watchdogLock) + { + DoRestart = AwaitingShutdown == ShutdownRequestPhase.None; + if (!DoRestart) + AwaitingShutdown = ShutdownRequestPhase.Pinged; + } + //Do this is a seperate thread or we'll kill this thread in the middle of rebooting + if (DoRestart) + ThreadPool.QueueUserWorkItem(_ => { Restart(); }); + else + ThreadPool.QueueUserWorkItem(_ => { Stop(); }); + } + //public api public string Restart() { @@ -151,6 +176,13 @@ namespace TGServerService while (true) { var starttime = DateTime.Now; + + lock (watchdogLock) + { + if (AwaitingShutdown == ShutdownRequestPhase.Requested) + SendCommand(SCGracefulShutdown); + } + Proc.WaitForExit(); lock (watchdogLock) @@ -160,7 +192,7 @@ namespace TGServerService Proc.Close(); ShutdownInterop(); - if (AwaitingShutdown) + if (AwaitingShutdown == ShutdownRequestPhase.Pinged) return; if ((DateTime.Now - starttime).Seconds < DDBadStartTime) @@ -205,7 +237,7 @@ namespace TGServerService { currentStatus = TGDreamDaemonStatus.Offline; currentPort = 0; - AwaitingShutdown = false; + AwaitingShutdown = ShutdownRequestPhase.None; if (!RestartInProgress) SendMessage("DD: Server stopped, watchdog exiting..."); } @@ -442,7 +474,7 @@ namespace TGServerService { lock (watchdogLock) { - return AwaitingShutdown; + return AwaitingShutdown != ShutdownRequestPhase.None; } } } diff --git a/TGServerService/Interop.cs b/TGServerService/Interop.cs index b08dbb0fea..28462a43a8 100644 --- a/TGServerService/Interop.cs +++ b/TGServerService/Interop.cs @@ -26,7 +26,7 @@ namespace TGServerService const string SCWorldAnnounce = "world_announce"; //sends param 'message' to the world const string SCIRCCheck = "irc_check"; //returns game stats const string SCIRCStatus = "irc_status"; //returns admin stats - const string SCNameCheck = "namecheck"; //returns keywords lookup + const string SCNameCheck = "namecheck"; //returns keywords lookup const string SCAdminPM = "adminmsg"; //pms a target ckey const string SCAdminWho = "adminwho"; //lists admins @@ -42,8 +42,7 @@ namespace TGServerService SendMessage("GAME: " + String.Join(" ", splits)); break; case "killme": - //Do this is a seperate thread or we'll kill this thread in the middle of rebooting - ThreadPool.QueueUserWorkItem(_ => { Restart(); }); + KillMe(); break; case "send2irc": splits.RemoveAt(0);