diff --git a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs index d9f9fb9f26..3ded84429a 100644 --- a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs @@ -69,6 +69,11 @@ namespace Tgstation.Server.Host.Components.Session /// Task OnReboot { get; } + /// + /// A that must complete before a TgsReboot() bridge request can complete. + /// + Task RebootGate { set; } + /// /// A that completes when the server calls /world/TgsInitializationComplete(). /// diff --git a/src/Tgstation.Server.Host/Components/Session/SessionController.cs b/src/Tgstation.Server.Host/Components/Session/SessionController.cs index 8b120bec72..36ef827390 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionController.cs @@ -75,6 +75,26 @@ namespace Tgstation.Server.Host.Components.Session /// public Task OnReboot => rebootTcs.Task; + /// + public Task RebootGate + { + get => rebootGate; + set + { + var tcs = new TaskCompletionSource(); + Task toAwait = null; + async Task Wrap() + { + await tcs.Task; + await toAwait; + await value; + } + + toAwait = Interlocked.Exchange(ref rebootGate, Wrap()); + tcs.SetResult(); + } + } + /// public Task OnPrime => primeTcs.Task; @@ -164,6 +184,11 @@ namespace Tgstation.Server.Host.Components.Session /// volatile TaskCompletionSource primeTcs; + /// + /// Backing field for . + /// + volatile Task rebootGate; + /// /// The number of currently active calls to from TgsReboot(). /// @@ -254,6 +279,8 @@ namespace Tgstation.Server.Host.Components.Session rebootTcs = new TaskCompletionSource(); primeTcs = new TaskCompletionSource(); + rebootGate = Task.CompletedTask; + // Run this asynchronously because we want to try to avoid any effects sending topics to the server while the initial bridge request is processing // It MAY be the source of a DD crash. See this gist https://gist.github.com/Cyberboss/7776bbeff3a957d76affe0eae95c9f14 // Worth further investigation as to if that sequence of events is a reliable crash vector and opening a BYOND bug if it is @@ -751,6 +778,7 @@ namespace Tgstation.Server.Host.Components.Session } Interlocked.Exchange(ref rebootTcs, new TaskCompletionSource()).SetResult(); + await RebootGate; } finally { diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index 126f36628e..559e6a11a5 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -804,6 +804,8 @@ namespace Tgstation.Server.Host.Components.Watchdog var ranInitialDmbCheck = false; for (ulong iteration = 1; nextAction != MonitorAction.Exit; ++iteration) using (LogContext.PushProperty(SerilogContextHelper.WatchdogMonitorIterationContextProperty, iteration)) + { + TaskCompletionSource nextMonitorWakeupTcs = new TaskCompletionSource(); try { Logger.LogTrace("Iteration {iteration} of monitor loop", iteration); @@ -821,6 +823,7 @@ namespace Tgstation.Server.Host.Components.Watchdog oldTask = newTaskFactory(); } + controller.RebootGate = nextMonitorWakeupTcs.Task; if (lastController == controller) { TryUpdateTask(ref activeServerLifetime, () => controller.Lifetime); @@ -967,6 +970,11 @@ namespace Tgstation.Server.Host.Components.Watchdog nextAction = MonitorAction.Continue; } } + finally + { + nextMonitorWakeupTcs.SetResult(); + } + } } catch (OperationCanceledException) {