2using System.Collections.Generic;
 
    3using System.Diagnostics;
 
    5using System.Threading.Tasks;
 
    7using Microsoft.Extensions.Logging;
 
   86            IMetricFactory metricFactory,
 
   89            ILogger<AdvancedWatchdog> logger,
 
   91            Api.Models.Instance instance,
 
   95                sessionControllerFactory,
 
  107                initialLaunchParameters,
 
  113                LinkFactory = linkFactory ?? 
throw new ArgumentNullException(nameof(linkFactory));
 
  121                Debug.Assert(disposeTask.IsCompleted, 
"This should always be true during construction!");
 
  122                disposeTask.GetAwaiter().GetResult();
 
 
  131            await base.DisposeAndNullControllersImpl();
 
 
  146        protected sealed override async ValueTask<MonitorAction> 
HandleNormalReboot(CancellationToken cancellationToken)
 
  156                    if (!controller.ProcessingRebootBridgeRequest)
 
  160                            "The reboot bridge request completed before the watchdog could suspend the server! This can lead to buggy DreamDaemon behaviour and should be reported! To ensure stability, we will need to hard reboot the server");
 
  168                        Logger.LogInformation(
"Deployed .dme is not ready to swap, delaying until next reboot!");
 
  169                        Chat.
QueueWatchdogMessage(
"The pending deployment was not ready to be activated this reboot. It will be applied at the next one.");
 
  178                var currentCompileJobId = controller.ReattachInformation.Dmb.CompileJob.Id;
 
  183                var localDeploymentCleanupGate = 
new TaskCompletionSource();
 
  184                async Task CleanupLingeringDeployment()
 
  188                        "Holding old deployment {compileJobId} for up to {expiry} seconds...",
 
  190                        lingeringDeploymentExpirySeconds);
 
  193                    var timeout = 
AsyncDelayer.
Delay(TimeSpan.FromSeconds(lingeringDeploymentExpirySeconds), CancellationToken.None).AsTask();
 
  195                    var completedTask = await Task.WhenAny(
 
  196                        localDeploymentCleanupGate.Task,
 
  199                    var timedOut = completedTask == timeout;
 
  204                        "Releasing old deployment {compileJobId}{afterTimeout}",
 
  210                    await lingeringDeployment.DisposeAsync();
 
  213                var oldDeploymentCleanupGate = Interlocked.Exchange(ref 
deploymentCleanupGate, localDeploymentCleanupGate);
 
  214                oldDeploymentCleanupGate?.TrySetResult();
 
  216                Logger.LogTrace(
"Replacing activeSwappable with pendingSwappable...");
 
  222                        CleanupLingeringDeployment());
 
  232                Logger.LogTrace(
"Nothing to do as pendingSwappable is null.");
 
  234            return await base.HandleNormalReboot(cancellationToken);
 
 
  242            if (canSeamlesslySwap)
 
  247                        "Not swapping to new compile job {compileJobId} as it uses a different engine version ({newEngineVersion}) than what is currently active {oldEngineVersion}.",
 
  251                    canSeamlesslySwap = 
false;
 
  256                        "Not swapping to new compile job {compileJobId} as it uses a different .dmb name ({newDmbName}) than what is currently active {oldDmbName}.",
 
  260                    canSeamlesslySwap = 
false;
 
  263            if (!canSeamlesslySwap)
 
  265                Logger.LogDebug(
"Queueing graceful restart instead...");
 
  266                await compileJobProvider.DisposeAsync();
 
  267                await base.HandleNewDmbAvailable(cancellationToken);
 
  277                    Logger.LogWarning(
"Active compile job has no DMAPI! Commencing immediate .dmb swap. Note this behavior is known to be buggy in some DM code contexts. See https://github.com/tgstation/tgstation-server/issues/1550");
 
  283                Logger.LogError(ex, 
"Exception while swapping");
 
  284                IDmbProvider providerToDispose = swappableProvider ?? compileJobProvider;
 
  285                await providerToDispose.DisposeAsync();
 
 
  297                throw new InvalidOperationException(
"Expected activeSwappable to be null!");
 
  299                throw new InvalidOperationException(
"Expected pendingSwappable to be null!");
 
  301            Logger.LogTrace(
"Prep for server launch");
 
  313                Logger.LogTrace(ex, 
"Initial link error, nulling ActiveSwappable");
 
 
  339            await base.SessionStartupPersist(cancellationToken);
 
 
  345            var result = await base.HandleMonitorWakeup(reason, cancellationToken);
 
 
  376            Logger.LogTrace(
"Linking compile job...");
 
 
  392            var suspended = 
false;
 
  396                server.SuspendProcess();
 
  401                Logger.LogWarning(ex, 
"Exception while suspending server!");
 
  407                await newProvider.
MakeActive(cancellationToken);
 
  413                    server.ResumeProcess();
 
 
  424            Logger.LogTrace(
"DrainDeploymentCleanupTasks...");
 
  426            localDeploymentCleanupGate?.TrySetResult();
 
  428            List<Task> localDeploymentCleanupTasks;
 
  432                localDeploymentCleanupTasks = 
new List<Task>(totalActiveTasks);
 
  433                for (var i = totalActiveTasks - 1; i >= 0; --i)
 
  436                    if (!blocking && !currentTask.IsCompleted)
 
  439                    localDeploymentCleanupTasks.Add(currentTask);
 
  444            return Task.WhenAll(localDeploymentCleanupTasks);
 
 
 
 
EngineType? Engine
The EngineType.
 
virtual ? long Id
The ID of the entity.
 
string? DmeName
The .dme file used for compilation.
 
Launch settings for DreamDaemon.
 
uint? StartupTimeout
The DreamDaemon startup timeout in seconds.
 
IDmbProvider LockNextDmb(string reason, [CallerFilePath] string? callerFile=null, [CallerLineNumber] int callerLine=default)
Gets the next IDmbProvider. DmbAvailable is a precondition.A new IDmbProvider.
 
A IDmbProvider that uses filesystem links to change directory structure underneath the server process...
 
Models.CompileJob CompileJob
The CompileJob of the .dmb.
 
Task FinishActivationPreparation(CancellationToken cancellationToken)
Should be awaited. before calling MakeActive(CancellationToken) to ensure the SwappableDmbProvider is...
 
bool Swapped
If MakeActive(CancellationToken) has been run.
 
ValueTask MakeActive(CancellationToken cancellationToken)
Make the SwappableDmbProvider active by replacing the live link with our CompileJob.
 
virtual ValueTask DisposeAsync()
 
ValueTask Update(ReattachInformation reattachInformation, CancellationToken cancellationToken)
Update some reattachInformation .A ValueTask representing the running operation.
 
A IWatchdog that, instead of killing servers for updates, uses the wonders of filesystem links to swa...
 
readonly List< Task > deploymentCleanupTasks
List<T> of Tasks that are waiting to clean up old deployments.
 
override async ValueTask< IDmbProvider > PrepServerForLaunch(IDmbProvider dmbToUse, CancellationToken cancellationToken)
Prepare the server to launch a new instance with the WatchdogBase.ActiveLaunchParameters and a given ...
 
bool CanUseSwappableDmbProvider(IDmbProvider dmbProvider)
If the SwappableDmbProvider feature of the AdvancedWatchdog can be used with a given dmbProvider .
 
SwappableDmbProvider? pendingSwappable
The active SwappableDmbProvider for WatchdogBase.ActiveLaunchParameters.
 
async ValueTask InitialLink(CancellationToken cancellationToken)
Create the initial link to the live game directory using ActiveSwappable.
 
SwappableDmbProvider? ActiveSwappable
The SwappableDmbProvider for WatchdogBase.LastLaunchParameters.
 
override async ValueTask< MonitorAction > HandleNormalReboot(CancellationToken cancellationToken)
Handler for MonitorActivationReason.ActiveServerRebooted when the RebootState is RebootState....
 
Task DrainDeploymentCleanupTasks(bool blocking)
Asynchronously drain deploymentCleanupTasks.
 
SwappableDmbProvider CreateSwappableDmbProvider(IDmbProvider dmbProvider)
Create a SwappableDmbProvider for a given dmbProvider .
 
override async ValueTask SessionStartupPersist(CancellationToken cancellationToken)
Called to save the current Server into the WatchdogBase.SessionPersistor when initially launched....
 
override async ValueTask DisposeAndNullControllersImpl()
 
override async ValueTask HandleNewDmbAvailable(CancellationToken cancellationToken)
Handler for MonitorActivationReason.NewDmbAvailable.A ValueTask representing the running operation.
 
IFilesystemLinkFactory LinkFactory
The IFilesystemLinkFactory for the AdvancedWatchdog.
 
volatile? TaskCompletionSource deploymentCleanupGate
The TaskCompletionSource representing the cleanup of an unused IDmbProvider.
 
async ValueTask PerformDmbSwap(SwappableDmbProvider newProvider, CancellationToken cancellationToken)
Suspends the BasicWatchdog.Server and calls SwappableDmbProvider.MakeActive(CancellationToken) on a n...
 
ValueTask ApplyInitialDmb(CancellationToken cancellationToken)
Set the ReattachInformation.InitialDmb for the BasicWatchdog.Server.
 
AdvancedWatchdog(IChatManager chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, ISessionPersistor sessionPersistor, IJobManager jobManager, IServerControl serverControl, IAsyncDelayer asyncDelayer, IIOManager diagnosticsIOManager, IEventConsumer eventConsumer, IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory, IMetricFactory metricFactory, IIOManager gameIOManager, IFilesystemLinkFactory linkFactory, ILogger< AdvancedWatchdog > logger, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart)
Initializes a new instance of the AdvancedWatchdog class.
 
override async ValueTask< MonitorAction > HandleMonitorWakeup(MonitorActivationReason reason, CancellationToken cancellationToken)
 
A IWatchdog that manages one server.
 
readonly IJobManager jobManager
The IJobManager for the WatchdogBase.
 
ILogger< WatchdogBase > Logger
The ILogger for the WatchdogBase.
 
Models.? CompileJob ActiveCompileJob
Retrieves the Models.CompileJob currently running on the server.
 
readonly bool autoStart
If the WatchdogBase should LaunchNoLock(bool, bool, bool, ReattachInformation, CancellationToken) in ...
 
readonly IEventConsumer eventConsumer
The IEventConsumer that is not the WatchdogBase.
 
async ValueTask DisposeAsync()
 
readonly IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory
The IRemoteDeploymentManagerFactory for the WatchdogBase.
 
readonly IIOManager diagnosticsIOManager
The IIOManager pointing to the Diagnostics directory.
 
DreamDaemonLaunchParameters ActiveLaunchParameters
The DreamDaemonLaunchParameters to be applied.
 
async ValueTask BeforeApplyDmb(Models.CompileJob newCompileJob, CancellationToken cancellationToken)
To be called before a given newCompileJob  goes live.
 
IChatManager Chat
The IChatManager for the WatchdogBase.
 
ValueTask Restart()
Restarts the Host.A ValueTask representing the running operation.
 
async ValueTask Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .A ValueTask representing the running operation.
 
For managing connected chat services.
 
void QueueWatchdogMessage(string message)
Queue a chat message  to configured watchdog channels.
 
Factory for IDmbProviders.
 
Provides absolute paths to the latest compiled .dmbs.
 
EngineVersion EngineVersion
The Api.Models.EngineVersion used to build the .dmb.
 
Models.CompileJob CompileJob
The CompileJob of the .dmb.
 
Factory for creating IRemoteDeploymentManagers.
 
Consumes EventTypes and takes the appropriate actions.
 
Factory for ISessionControllers.
 
Handles saving and loading ReattachInformation.
 
Represents a service that may take an updated Host assembly and run it, stopping the current assembly...
 
For creating filesystem symbolic links.
 
Interface for using filesystems.
 
Manages the runtime of Jobs.
 
For waiting asynchronously.
 
EngineType
The type of engine the codebase is using.
 
MonitorAction
The action for the monitor loop to take when control is returned to it.
 
MonitorActivationReason
Reasons for the monitor to wake up.