Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MoryxService to handle closing the console window. #357

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<PackageReference Update="Microsoft.Extensions.Logging" Version="$(dotnetVersion)" />
<PackageReference Update="Microsoft.Extensions.Configuration.Json" Version="$(dotnetVersion)" />
<PackageReference Update="Microsoft.Extensions.Configuration.FileExtensions" Version="$(dotnetVersion)" />
<PackageReference Update="Microsoft.Extensions.Hosting" Version="$(dotnetVersion)" />
<PackageReference Update="Microsoft.Data.Sqlite" Version="$(dotnetVersion)" />

<PackageReference Update="Microsoft.EntityFrameworkCore" Version="$(efCoreVersion)" />
Expand Down
9 changes: 9 additions & 0 deletions src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ public static void AddMoryxModules(this IServiceCollection serviceCollection)
}
}

/// <summary>
/// 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.
/// </summary>
public static void AddMoryxService(this IServiceCollection serviceCollection)
{
serviceCollection.AddHostedService<MoryxHost>();
}

/// <summary>
/// Use the moryx config manager
/// </summary>
Expand Down
5 changes: 4 additions & 1 deletion src/Moryx.Runtime.Kernel/Moryx.Runtime.Kernel.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
<PackageTags>MORYX;Kernel;Runtime;Server</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Moryx\Moryx.csproj" />
<ProjectReference Include="..\Moryx.Container\Moryx.Container.csproj" />
<ProjectReference Include="..\Moryx.Runtime\Moryx.Runtime.csproj" />
</ItemGroup>

</Project>

103 changes: 103 additions & 0 deletions src/Moryx.Runtime.Kernel/MoryxHost.cs
Original file line number Diff line number Diff line change
@@ -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<MoryxHost> 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
}

/// <summary>
/// Callback that is called when a console event is raised.
/// </summary>
/// <param name="ctrlType"></param>
/// <returns></returns>
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<MoryxHost> 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<MoryxHostState> 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);
}
}
}
Loading