Files
RobustToolbox/Robust.Shared/SignalHandler.cs
Pieter-Jan Briers 4cb9bd3332 Deduplicate IoC init.
2019-07-13 21:51:57 +02:00

134 lines
4.5 KiB
C#

using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading;
using Robust.Shared.Asynchronous;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Log;
namespace Robust.Shared
{
internal abstract class SignalHandler : ISignalHandler, IDisposable, IPostInjectInit
{
#pragma warning disable 649
[Dependency] private readonly ITaskManager _taskManager;
[Dependency] private readonly IConfigurationManager _configurationManager;
#pragma warning restore 649
private Thread _signalThread;
public void PostInject()
{
_configurationManager.RegisterCVar("signals.handle", true);
}
public void MaybeStart()
{
// I actually did try to implement a onValueChanged handler but couldn't make it work well.
// The problem is that shutting down the thread does not restore the default exit behavior.
#if UNIX
if (_configurationManager.GetCVar<bool>("signals.handle"))
{
Start();
}
#endif
}
[SuppressMessage("ReSharper", "IdentifierTypo")]
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "CommentTypo")]
private void Start()
{
var runningOnMono = Type.GetType("Mono.Runtime") != null;
if (!runningOnMono)
{
return;
}
try
{
// Reflection is fun.
var assembly = FindMonoPosix();
Logger.Debug("Successfully loaded Mono.Posix. Registering signal handlers...");
var signalType = assembly.GetType("Mono.Unix.UnixSignal");
// Mono.Unix.UnixSignal[]
var signalArrayType = signalType.MakeArrayType();
var signumType = assembly.GetType("Mono.Unix.Native.Signum");
var SIGTERM = Enum.Parse(signumType, "SIGTERM");
var SIGINT = Enum.Parse(signumType, "SIGINT");
// int UnixSignal.WaitAny(UnixSignal[])
var WaitAny = signalType.GetMethod("WaitAny", new Type[] {signalArrayType});
// UnixSignal.Signum
var Signum = signalType.GetProperty("Signum");
var signals = Array.CreateInstance(signalType, 2);
signals.SetValue(Activator.CreateInstance(signalType, SIGTERM), 0);
signals.SetValue(Activator.CreateInstance(signalType, SIGINT), 1);
_signalThread = new Thread(() =>
{
while (true)
{
var args = new object[] {signals};
// int UnixSignal.WaitAny(UnixSignal[])
// ReSharper disable once PossibleNullReferenceException
var index = (int) WaitAny.Invoke(null, args);
// signals[index].Signum
// ReSharper disable once PossibleNullReferenceException
var signum = Signum.GetValue(signals.GetValue(index), null).ToString();
// Can't use switch with reflection. Shame.
// Tried to compare the objects directly. Didn't work.
// String it is.
if (signum == "SIGINT" || signum == "SIGTERM")
{
_taskManager.RunOnMainThread(() => OnReceiveTerminationSignal(signum));
}
}
// ReSharper disable once FunctionNeverReturns
})
{
IsBackground = true,
Name = "signal handler"
};
_signalThread.Start();
}
catch (Exception e)
{
Logger.Error("Running on mono but couldn't register signal handlers: {0}", e);
}
}
private void Stop()
{
_signalThread?.Abort();
_signalThread = null;
}
protected abstract void OnReceiveTerminationSignal(string signal);
private static Assembly FindMonoPosix()
{
// This works don't touch it.
// Well it works on MacOS. Can't speak about Linux.
return Assembly.Load("../Mono.Posix.dll");
}
public void Dispose()
{
Stop();
}
}
internal interface ISignalHandler
{
void MaybeStart();
}
}