diff --git a/Directory.Build.targets b/Directory.Build.targets index d12d3f6bd..61e496c59 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -34,6 +34,7 @@ + diff --git a/src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs b/src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs index 83bc5b833..8275f2db1 100644 --- a/src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs +++ b/src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs @@ -74,6 +74,15 @@ public static void AddMoryxModules(this IServiceCollection serviceCollection) } } + /// + /// Register BackgroundService that controls that starts and stops the MORYX modules inside the lifetime of the host. + /// The MORYX Service can hook into the command line and gracefuly stop the modules when the user tries to close the window. + /// + public static void AddMoryxService(this IServiceCollection serviceCollection) + { + serviceCollection.AddHostedService(); + } + /// /// Use the moryx config manager /// diff --git a/src/Moryx.Runtime.Kernel/Moryx.Runtime.Kernel.csproj b/src/Moryx.Runtime.Kernel/Moryx.Runtime.Kernel.csproj index 054c7834f..650956a63 100644 --- a/src/Moryx.Runtime.Kernel/Moryx.Runtime.Kernel.csproj +++ b/src/Moryx.Runtime.Kernel/Moryx.Runtime.Kernel.csproj @@ -7,11 +7,14 @@ MORYX;Kernel;Runtime;Server + + + + - \ No newline at end of file diff --git a/src/Moryx.Runtime.Kernel/MoryxHost.cs b/src/Moryx.Runtime.Kernel/MoryxHost.cs new file mode 100644 index 000000000..16344b7e1 --- /dev/null +++ b/src/Moryx.Runtime.Kernel/MoryxHost.cs @@ -0,0 +1,103 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Moryx.Runtime.Modules; +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Moryx.Runtime.Kernel +{ + internal class MoryxHost : BackgroundService + { + private readonly IModuleManager moduleManager; + private readonly IHost lifeTime; + private readonly ILogger logger; + + // Console shutdown handling according to https://stackoverflow.com/questions/21751545/how-to-make-a-console-app-exit-gracefully-when-it-is-closed + [DllImport("Kernel32")] + public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add); + + // A delegate type to be used as the handler routine + // for SetConsoleCtrlHandler. + public delegate bool HandlerRoutine(CtrlTypes CtrlType); + + // An enumerated type for the control messages + // sent to the handler routine. + public enum CtrlTypes + { + CTRL_C_EVENT = 0, + CTRL_BREAK_EVENT, + CTRL_CLOSE_EVENT, + CTRL_LOGOFF_EVENT = 5, + CTRL_SHUTDOWN_EVENT + } + + /// + /// Callback that is called when a console event is raised. + /// + /// + /// + private bool ConsoleCtrlCheck(CtrlTypes ctrlType) + { + // Stop the host and await shutdown. + lifeTime.StopAsync(TimeSpan.FromSeconds(100)).ContinueWith(task => + { + if (task.IsFaulted) + { + logger.LogError(task.Exception, "Received failure on shutdown."); + } + }).Wait(); + return true; + } + + public MoryxHost(IModuleManager moduleManager, IHost lifetTime, ILogger logger) + { + this.moduleManager = moduleManager; + this.lifeTime = lifetTime; + this.logger = logger; + } + public enum MoryxHostState { + NotStarted, + Starting, + Started, + Stopping, + Stopped + } + + public MoryxHostState State {get;private set;} + public event EventHandler StateChanged; + + + public override async Task StartAsync(CancellationToken cancellationToken) + { + State = MoryxHostState.Starting; + StateChanged?.Invoke(this, State); + // Only register on windows, because the behavior is os specific + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + SetConsoleCtrlHandler(ConsoleCtrlCheck, true); + } + moduleManager.StartModules(); + + await base.StartAsync(cancellationToken); + + State = MoryxHostState.Started; + StateChanged?.Invoke(this, State); + } + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + return Task.CompletedTask; + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + State = MoryxHostState.Stopping; + StateChanged?.Invoke(this, State); + moduleManager.StopModules(); + await base.StopAsync(cancellationToken); + State = MoryxHostState.Stopped; + StateChanged?.Invoke(this, State); + } + } +}