From e9e4439a861290eee2918bf5c03e6de5b85d3b18 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Thu, 14 Jan 2021 11:38:19 -0500 Subject: [PATCH] Adds a few event types - Improves watchdog monitoring logic so events that happen while one is already processing are no longer dropped. --- src/DMAPI/tgs.dm | 7 +-- .../Components/Events/EventType.cs | 14 ++++- .../Components/Session/ISessionController.cs | 4 +- .../Components/Watchdog/BasicWatchdog.cs | 8 ++- .../Watchdog/MonitorActivationReason.cs | 7 ++- .../Components/Watchdog/WatchdogBase.cs | 53 ++++++++++++++++--- 6 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/DMAPI/tgs.dm b/src/DMAPI/tgs.dm index fb6261a9bc..f54d519b9e 100644 --- a/src/DMAPI/tgs.dm +++ b/src/DMAPI/tgs.dm @@ -95,10 +95,12 @@ #define TGS_EVENT_WATCHDOG_SHUTDOWN 15 /// Before the watchdog detaches for a TGS update/restart. No parameters. #define TGS_EVENT_WATCHDOG_DETACH 16 -// We don't actually implement this value as the DMAPI can never receive it +// We don't actually implement these 3 events as the DMAPI can never receive them. // #define TGS_EVENT_WATCHDOG_LAUNCH 17 -// Same here. We don't actually implement this value as the DMAPI can never receive it // #define TGS_EVENT_WORLD_END_PROCESS 18 +// #define TGS_EVENT_WORLD_DEL 19 +/// Watchdog event when TgsInitializationComplete() is called. No parameters. + #define TGS_EVENT_WORLD_PRIME 20 // OTHER ENUMS @@ -132,7 +134,6 @@ * * This may use [/world/var/sleep_offline] to make this happen so ensure no changes are made to it while this call is running. * Afterwards, consider explicitly setting it to what you want to avoid this BYOND bug: http://www.byond.com/forum/post/2575184 - * Before this point, note that any static files or directories may be in use by another server. Your code should account for this. * This function should not be called before ..() in [/world/proc/New]. */ /world/proc/TgsInitializationComplete() diff --git a/src/Tgstation.Server.Host/Components/Events/EventType.cs b/src/Tgstation.Server.Host/Components/Events/EventType.cs index f22c1db8e0..d99ee079e2 100644 --- a/src/Tgstation.Server.Host/Components/Events/EventType.cs +++ b/src/Tgstation.Server.Host/Components/Events/EventType.cs @@ -114,9 +114,21 @@ namespace Tgstation.Server.Host.Components.Events WatchdogLaunch, /// - /// In between DD restarts if the process has been force-ended by the DMAPI (TgsEndProcess()) + /// In between watchdog DreamDaemon restarts if the process has been force-ended by the DMAPI (TgsEndProcess()). No parameters. /// [EventScript("WorldEndProcess")] WorldEndProcess, + + /// + /// After any sort of watchdog DreamDaemon closure or reboot. Reboots only supported with DMAPI. Not synchronous. Called after . No parameters. + /// + [EventScript("WorldDel")] + WorldDel, + + /// + /// Watchdog event when TgsInitializationComplete() is called. No parameters. + /// + [EventScript("WorldPrime")] + WorldPrime, } } diff --git a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs index 778c39ba38..9c7a43cb3a 100644 --- a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs @@ -49,12 +49,12 @@ namespace Tgstation.Server.Host.Components.Session RebootState RebootState { get; } /// - /// A that completes when the server calls /world/Reboot() + /// A that completes when the server calls /world/TgsReboot(). /// Task OnReboot { get; } /// - /// A that completes when the server calls /world/TgsInitializationsComplete() + /// A that completes when the server calls /world/TgsInitializationComplete() /// Task OnPrime { get; } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs index 433680a941..d8db5b4163 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs @@ -95,6 +95,8 @@ namespace Tgstation.Server.Host.Components.Watchdog if (Server.TerminationWasRequested) await EventConsumer.HandleEvent(EventType.WorldEndProcess, Enumerable.Empty(), cancellationToken).ConfigureAwait(false); + await EventConsumer.HandleEvent(EventType.WorldDel, Enumerable.Empty(), cancellationToken).ConfigureAwait(false); + string exitWord = Server.TerminationWasRequested ? "exited" : "crashed"; if (Server.RebootState == Session.RebootState.Shutdown) { @@ -128,6 +130,8 @@ namespace Tgstation.Server.Host.Components.Watchdog gracefulRebootRequired = false; Server.ResetRebootState(); + await EventConsumer.HandleEvent(EventType.WorldDel, Enumerable.Empty(), cancellationToken).ConfigureAwait(false); + switch (rebootState) { case Session.RebootState.Normal: @@ -144,7 +148,6 @@ namespace Tgstation.Server.Host.Components.Watchdog default: throw new InvalidOperationException($"Invalid reboot state: {rebootState}"); } - case MonitorActivationReason.ActiveLaunchParametersUpdated: await Server.SetRebootState(Session.RebootState.Restart, cancellationToken).ConfigureAwait(false); gracefulRebootRequired = true; @@ -152,6 +155,9 @@ namespace Tgstation.Server.Host.Components.Watchdog case MonitorActivationReason.NewDmbAvailable: await HandleNewDmbAvailable(cancellationToken).ConfigureAwait(false); break; + case MonitorActivationReason.ActiveServerPrimed: + await EventConsumer.HandleEvent(EventType.WorldPrime, Enumerable.Empty(), cancellationToken).ConfigureAwait(false); + break; case MonitorActivationReason.Heartbeat: default: throw new InvalidOperationException($"Invalid activation reason: {reason}"); diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs index 4b0d046561..4f1073e742 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs @@ -1,4 +1,4 @@ -namespace Tgstation.Server.Host.Components.Watchdog +namespace Tgstation.Server.Host.Components.Watchdog { /// /// Reasons for the monitor to wake up @@ -29,5 +29,10 @@ /// A heartbeat is required. /// Heartbeat, + + /// + /// Server primed. + /// + ActiveServerPrimed, } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index f6a7ed6160..3e89293cb3 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -609,6 +609,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// The for the operation. /// A representing the running operation. + #pragma warning disable CA1502 private async Task MonitorLifetimes(CancellationToken cancellationToken) { Logger.LogTrace("Entered MonitorLifetimes"); @@ -619,6 +620,12 @@ namespace Tgstation.Server.Host.Components.Watchdog try { MonitorAction nextAction = MonitorAction.Continue; + Task activeServerLifetime = null, + activeServerReboot = null, + serverPrimed = null, + activeLaunchParametersChanged = null, + newDmbAvailable = null; + ISessionController lastController = null; for (ulong iteration = 1; nextAction != MonitorAction.Exit; ++iteration) using (LogContext.PushProperty("Monitor", iteration)) try @@ -627,17 +634,44 @@ namespace Tgstation.Server.Host.Components.Watchdog nextAction = MonitorAction.Continue; var controller = GetActiveController(); - Task activeServerLifetime = controller.Lifetime; - var activeServerReboot = controller.OnReboot; - Task activeLaunchParametersChanged = ActiveParametersUpdated.Task; - var newDmbAvailable = DmbFactory.OnNewerDmb; + void UpdateMonitoredTasks() + { + static void TryUpdateTask(ref Task oldTask, Task newTask) + { + if (oldTask?.IsCompleted == true) + return; + + oldTask = newTask; + } + + if (lastController == controller) + { + TryUpdateTask(ref activeServerLifetime, controller.Lifetime); + TryUpdateTask(ref activeServerReboot, controller.OnReboot); + TryUpdateTask(ref serverPrimed, controller.OnPrime); + } + else + { + activeServerLifetime = controller.Lifetime; + activeServerReboot = controller.OnReboot; + serverPrimed = controller.OnPrime; + lastController = controller; + } + + TryUpdateTask(ref activeLaunchParametersChanged, ActiveParametersUpdated.Task); + TryUpdateTask(ref newDmbAvailable, DmbFactory.OnNewerDmb); + } + + UpdateMonitoredTasks(); var heartbeatSeconds = ActiveLaunchParameters.HeartbeatSeconds.Value; var heartbeat = heartbeatSeconds == 0 || !controller.DMApiAvailable ? Extensions.TaskExtensions.InfiniteTask() - : Task.Delay(TimeSpan.FromSeconds(heartbeatSeconds), cancellationToken); + : Task.Delay( + TimeSpan.FromSeconds(heartbeatSeconds), + cancellationToken); // cancel waiting if requested var cancelTcs = new TaskCompletionSource(); @@ -647,7 +681,8 @@ namespace Tgstation.Server.Host.Components.Watchdog heartbeat, newDmbAvailable, cancelTcs.Task, - activeLaunchParametersChanged); + activeLaunchParametersChanged, + serverPrimed); // wait for something to happen using (cancellationToken.Register(() => cancelTcs.SetCanceled())) @@ -688,7 +723,10 @@ namespace Tgstation.Server.Host.Components.Watchdog || CheckActivationReason(ref activeServerReboot, MonitorActivationReason.ActiveServerRebooted) || CheckActivationReason(ref newDmbAvailable, MonitorActivationReason.NewDmbAvailable) || CheckActivationReason(ref activeLaunchParametersChanged, MonitorActivationReason.ActiveLaunchParametersUpdated) - || CheckActivationReason(ref heartbeat, MonitorActivationReason.Heartbeat); + || CheckActivationReason(ref heartbeat, MonitorActivationReason.Heartbeat) + || CheckActivationReason(ref serverPrimed, MonitorActivationReason.ActiveServerPrimed); + + UpdateMonitoredTasks(); if (!anyActivation) moreActivationsToProcess = false; @@ -769,6 +807,7 @@ namespace Tgstation.Server.Host.Components.Watchdog Logger.LogTrace("Monitor exiting..."); } + #pragma warning restore CA1502 /// /// Starts all s.