2using System.Collections.Generic;
6using System.Threading.Tasks;
8using Microsoft.Extensions.DependencyInjection;
9using Microsoft.Extensions.Hosting;
10using Microsoft.Extensions.Logging;
11using Microsoft.Extensions.Options;
31#if WATCHDOG_FREE_RESTART
40 internal IHost? Host {
get;
private set; }
115 public async ValueTask
Run(CancellationToken cancellationToken)
119 using (var fsWatcher = updateDirectory !=
null ?
new FileSystemWatcher(updateDirectory) :
null)
121 if (fsWatcher !=
null)
128 fsWatcher.EnableRaisingEvents =
true;
135 logger = Host.Services.GetRequiredService<ILogger<Server>>();
138 using (cancellationToken.Register(() =>
logger.LogInformation(
"Server termination requested!")))
140 var generalConfigurationOptions = Host.Services.GetRequiredService<IOptions<GeneralConfiguration>>();
148 catch (OperationCanceledException ex)
150 logger.LogDebug(ex,
"Server run cancelled!");
175 ArgumentNullException.ThrowIfNull(updateExecutor);
176 ArgumentNullException.ThrowIfNull(newVersion);
181 throw new InvalidOperationException(
"Tried to start update when server was initialized without an updatePath set!");
183 var
logger = this.logger!;
184 logger.LogTrace(
"Begin ApplyUpdate...");
186 CancellationToken criticalCancellationToken;
191 logger.LogDebug(
"Aborted update due to concurrency conflict!");
196 throw new InvalidOperationException(
"Tried to update a non-running Server!");
202 async Task RunUpdate()
206 logger.LogTrace(
"Update complete!");
211 logger.LogTrace(
"Stopping host due to termination request...");
216 logger.LogTrace(
"Update failed!");
228 ArgumentNullException.ThrowIfNull(handler);
232 var
logger = this.logger!;
236 logger.LogTrace(
"Registering restart handler {handlerImplementationName}...", handler);
247 logger.LogWarning(
"Restart handler {handlerImplementationName} register after a shutdown had begun!", handler);
260 if (exception !=
null)
264 return ValueTask.CompletedTask;
274 throw new InvalidOperationException(
"Server restarts are not supported");
277 throw new InvalidOperationException(
"Tried to control a non-running Server!");
289 if (otherException !=
null)
303 async ValueTask
RestartImpl(Version? newVersion,
Exception? exception,
bool requireWatchdog,
bool completeAsap)
308 bool isGracefulShutdown = !requireWatchdog && exception ==
null;
309 var
logger = this.logger!;
311 "Begin {restartType}...",
314 ?
"semi-graceful shutdown"
315 :
"graceful shutdown"
322 logger.LogTrace(
"Aborted restart due to concurrency conflict!");
330 if (exception ==
null)
332 var giveHandlersTimeToWaitAround = isGracefulShutdown && !completeAsap;
333 logger.LogInformation(
"Stopping server...");
334 using var cts =
new CancellationTokenSource(
335 TimeSpan.FromMinutes(
336 giveHandlersTimeToWaitAround
339 var cancellationToken = cts.Token;
342 ValueTask eventsTask;
347 x => x.HandleRestart(newVersion, giveHandlersTimeToWaitAround, cancellationToken))
350 logger.LogTrace(
"Joining restart handlers...");
353 catch (OperationCanceledException ex)
355 if (isGracefulShutdown)
356 logger.LogWarning(ex,
"Graceful shutdown timeout hit! Existing DreamDaemon processes will be terminated!");
360 "Restart timeout hit! Existing DreamDaemon processes will be lost and must be killed manually before being restarted with TGS!");
364 logger.LogError(e,
"Restart handlers error!");
378 logger?.LogTrace(
"FileSystemWatcher triggered.");
381 if (eventArgs.FullPath == Path.GetFullPath(
updatePath!) && File.Exists(eventArgs.FullPath))
383 logger?.LogInformation(
"Host watchdog appears to be requesting server termination!");
395 logger?.LogInformation(
"An update is in progress, we will wait for that to complete...");
405 logger!.LogDebug(
"Stopping host...");
Extension methods for the ValueTask and ValueTask<TResult> classes.
static async ValueTask WhenAll(IEnumerable< ValueTask > tasks)
Fully await a given list of tasks .
General configuration options.
uint ShutdownTimeoutMinutes
The timeout minutes for gracefully stopping the server.
uint RestartTimeoutMinutes
The timeout minutes for restarting the server.
bool WatchdogPresent
true if live updates are supported, false. TryStartUpdate(IServerUpdateExecutor, Version) and Restart...
IRestartRegistration RegisterForRestart(IRestartHandler handler)
Register a given handler to run before stopping the server for a restart. A new IRestartRegistration...
ValueTask Die(Exception? exception)
Kill the server with a fatal exception. A Task representing the running operation.
bool UpdateInProgress
Whether or not the server is currently updating.
async ValueTask Run(CancellationToken cancellationToken)
Runs the IServer. A ValueTask representing the running operation.
bool TryStartUpdate(IServerUpdateExecutor updateExecutor, Version newVersion)
Attempt to update with a given updateExecutor . true if the update started successfully,...
Task? updateTask
The Task that is used for asynchronously updating the server.
async ValueTask RestartImpl(Version? newVersion, Exception? exception, bool requireWatchdog, bool completeAsap)
Implements Restart().
void CheckSanity(bool checkWatchdog)
Throws an InvalidOperationException if the IServerControl cannot be used.
ValueTask GracefulShutdown(bool detach)
Gracefully shutsdown the Host. A ValueTask representing the running operation.
readonly List< IRestartHandler > restartHandlers
The IRestartHandlers to run when the Server restarts.
CancellationTokenSource? cancellationTokenSource
The cancellationTokenSource for the Server.
ValueTask Restart()
Restarts the Host. A ValueTask representing the running operation.
bool terminateIfUpdateFails
If there is an update in progress and this flag is set, it should stop the server immediately if it f...
readonly? string updatePath
The absolute path to install updates to.
ILogger< Server >? logger
The ILogger for the Server.
void WatchForShutdownFileCreation(object sender, FileSystemEventArgs eventArgs)
Event handler for the updatePath's FileSystemWatcher. Triggers shutdown if requested by host watchdog...
readonly IHostBuilder hostBuilder
The IHostBuilder for the Server.
Exception? propagatedException
The Exception to propagate when the server terminates.
GeneralConfiguration? generalConfiguration
The GeneralConfiguration for the Server.
readonly object restartLock
lock object for certain restart related operations.
bool shutdownInProgress
If the server is being shut down or restarted.
Server(IHostBuilder hostBuilder, string? updatePath)
Initializes a new instance of the Server class.
bool RestartRequested
If the IServer should restart.
void StopServerImmediate()
Fires off the cancellationTokenSource without any checks, shutting down everything.
void CheckExceptionPropagation(Exception? otherException)
Re-throw propagatedException if it exists.
Runs a given disposeAction on Dispose.
Handler for server restarts.
Represents the lifetime of a IRestartHandler registration.
Represents a service that may take an updated Host assembly and run it, stopping the current assembly...
Executes server update operations.
ValueTask< bool > ExecuteUpdate(string updatePath, CancellationToken cancellationToken, CancellationToken criticalCancellationToken)
Executes a pending server update by extracting the new server to a given updatePath .