diff --git a/src/Tgstation.Server.Host/Components/IInstanceShutdownHandler.cs b/src/Tgstation.Server.Host/Components/IInstanceShutdownHandler.cs deleted file mode 100644 index 87264ca110..0000000000 --- a/src/Tgstation.Server.Host/Components/IInstanceShutdownHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading.Tasks; -using Tgstation.Server.Api.Models.Internal; - -namespace Tgstation.Server.Host.Components -{ - /// - /// For handling shutdowns - /// - public interface IInstanceShutdownHandler - { - //TODO - /// - /// OMG - /// - /// - /// - /// - /// - /// - Task PreserveActiveExecutablesIfNecessary(DreamDaemonLaunchParameters launchParameters, string accessToken, int pid, bool primary); - } -} diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index 1d9671acff..c58e0e972b 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -25,14 +25,22 @@ namespace Tgstation.Server.Host.Components /// The for the /// readonly IInstanceFactory instanceFactory; + /// /// The for the /// readonly IIOManager ioManager; + /// /// The for the /// readonly IDatabaseContextFactory databaseContextFactory; + + /// + /// The for the + /// + readonly IApplication application; + /// /// Map of s to respective s /// @@ -48,11 +56,14 @@ namespace Tgstation.Server.Host.Components /// The value of /// The value of /// The value of - public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory) + /// The value of + public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application) { this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory)); this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); + this.application = application ?? throw new ArgumentNullException(nameof(application)); + instances = new Dictionary(); interopConsumers = new Dictionary(); } @@ -129,11 +140,19 @@ namespace Tgstation.Server.Host.Components /// public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext => { - await databaseContext.Initialize(cancellationToken).ConfigureAwait(false); - var dbInstances = databaseContext.Instances.Where(x => x.Online.Value).Include(x => x.RepositorySettings).Include(x => x.ChatSettings).Include(x => x.DreamDaemonSettings).ToAsyncEnumerable(); - var tasks = new List(); - await dbInstances.ForEachAsync(metadata => tasks.Add(OnlineInstance(metadata, cancellationToken)), cancellationToken).ConfigureAwait(false); - await Task.WhenAll(tasks).ConfigureAwait(false); + try + { + await databaseContext.Initialize(cancellationToken).ConfigureAwait(false); + var dbInstances = databaseContext.Instances.Where(x => x.Online.Value).Include(x => x.RepositorySettings).Include(x => x.ChatSettings).Include(x => x.DreamDaemonSettings).ToAsyncEnumerable(); + var tasks = new List(); + await dbInstances.ForEachAsync(metadata => tasks.Add(OnlineInstance(metadata, cancellationToken)), cancellationToken).ConfigureAwait(false); + await Task.WhenAll(tasks).ConfigureAwait(false); + application.Ready(null); + } + catch (Exception e) + { + application.Ready(e); + } }); /// @@ -143,16 +162,6 @@ namespace Tgstation.Server.Host.Components instances.Clear(); } - /// - public Task PreserveActiveExecutablesIfNecessary(DreamDaemonLaunchParameters launchParameters, string accessToken, int pid, bool primary) - { - if (launchParameters == null) - throw new ArgumentNullException(nameof(launchParameters)); - if (accessToken == null) - throw new ArgumentNullException(nameof(accessToken)); - throw new NotImplementedException(); - } - /// public IInteropContext Register(string accessIdentifier, IInteropConsumer consumer) { diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index b70dd81236..23cbaab64a 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -1,4 +1,5 @@ using Byond.TopicSender; +using Cyberboss.AspNetCore.AsyncInitializer; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -16,6 +17,7 @@ using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Components.Chat.Commands; using Tgstation.Server.Host.Components.Watchdog; @@ -54,6 +56,8 @@ namespace Tgstation.Server.Host.Core /// readonly Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment; + readonly TaskCompletionSource startupTcs; + /// /// The for the /// @@ -69,6 +73,8 @@ namespace Tgstation.Server.Host.Core this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); + startupTcs = new TaskCompletionSource(); + Version = Assembly.GetExecutingAssembly().GetName().Version; VersionString = String.Format(CultureInfo.InvariantCulture, "{0} v{1}", VersionPrefix, Version); } @@ -212,9 +218,26 @@ namespace Tgstation.Server.Host.Core if (hostingEnvironment.IsDevelopment()) applicationBuilder.UseDeveloperExceptionPage(); - + + applicationBuilder.UseAsyncInitialization(async cancellationToken => + { + using (cancellationToken.Register(() => startupTcs.SetCanceled())) + await startupTcs.Task.ConfigureAwait(false); + }); + applicationBuilder.UseAuthentication(); applicationBuilder.UseMvc(); } + + /// + public void Ready(Exception initializationError) + { + if (startupTcs.Task.IsCompleted) + throw new InvalidOperationException("Ready has already been called!"); + if (initializationError == null) + startupTcs.SetResult(null); + else + startupTcs.SetException(initializationError); + } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Core/IApplication.cs b/src/Tgstation.Server.Host/Core/IApplication.cs index b00ae8a3b9..1cbe820b4c 100644 --- a/src/Tgstation.Server.Host/Core/IApplication.cs +++ b/src/Tgstation.Server.Host/Core/IApplication.cs @@ -21,5 +21,11 @@ namespace Tgstation.Server.Host.Core /// The url the server can be reached at locally /// string HostingPath { get; } + + /// + /// Mark the as ready to run + /// + /// The that put the application in a corrupted state if any + void Ready(Exception initializationError); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 53b174f006..5720992891 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -35,6 +35,7 @@ +