From c2087af8c95b2aa7c837d65c5815801b6ee7826c Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 9 Mar 2014 22:35:50 -0700 Subject: [PATCH] Gracefully exit on restart instead of forcibly killing it --- .../Messaging/MessageBroker.cs | 8 ++++ src/NzbDrone.Api/Config/HostConfigResource.cs | 1 + .../EnvironmentInfo/RuntimeInfo.cs | 2 + .../EnvironmentInfo/StartupContext.cs | 1 + .../Configuration/ConfigFileProvider.cs | 6 +++ .../Lifecycle/LifecycleService.cs | 10 ----- src/NzbDrone.Host/ApplicationServer.cs | 13 ++++--- src/NzbDrone.Host/Bootstrap.cs | 13 ++++--- src/NzbDrone.Host/NzbDrone.Host.csproj | 1 + src/NzbDrone.Host/SpinService.cs | 36 ++++++++++++++++++ .../Settings/General/GeneralViewTemplate.html | 23 +++++++++++ src/UpgradeLog.htm | Bin 60810 -> 0 bytes src/UpgradeLog2.htm | Bin 60810 -> 0 bytes 13 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 src/NzbDrone.Host/SpinService.cs delete mode 100644 src/UpgradeLog.htm delete mode 100644 src/UpgradeLog2.htm diff --git a/src/Microsoft.AspNet.SignalR.Core/Messaging/MessageBroker.cs b/src/Microsoft.AspNet.SignalR.Core/Messaging/MessageBroker.cs index 76c24161d..8995f96bc 100644 --- a/src/Microsoft.AspNet.SignalR.Core/Messaging/MessageBroker.cs +++ b/src/Microsoft.AspNet.SignalR.Core/Messaging/MessageBroker.cs @@ -295,6 +295,14 @@ namespace Microsoft.AspNet.SignalR.Messaging Trace.TraceEvent(TraceEventType.Verbose, 0, "Dispoing the broker"); + //Check if OS is not Windows and exit + var platform = (int)Environment.OSVersion.Platform; + + if ((platform == 4) || (platform == 6) || (platform == 128)) + { + return; + } + // Wait for all threads to stop working WaitForDrain(); diff --git a/src/NzbDrone.Api/Config/HostConfigResource.cs b/src/NzbDrone.Api/Config/HostConfigResource.cs index 8fc4151d9..3387ba421 100644 --- a/src/NzbDrone.Api/Config/HostConfigResource.cs +++ b/src/NzbDrone.Api/Config/HostConfigResource.cs @@ -14,6 +14,7 @@ namespace NzbDrone.Api.Config public String Password { get; set; } public String LogLevel { get; set; } public String Branch { get; set; } + public Boolean AutoUpdate { get; set; } public String ApiKey { get; set; } public Boolean Torrent { get; set; } public String SslCertHash { get; set; } diff --git a/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs b/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs index 8cef57af6..679fe2f1c 100644 --- a/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs +++ b/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs @@ -16,6 +16,7 @@ namespace NzbDrone.Common.EnvironmentInfo bool IsWindowsService { get; } bool IsConsole { get; } bool IsRunning { get; set; } + bool RestartPending { get; set; } string ExecutingApplication { get; } } @@ -83,6 +84,7 @@ namespace NzbDrone.Common.EnvironmentInfo } public bool IsRunning { get; set; } + public bool RestartPending { get; set; } public string ExecutingApplication { get; private set; } public static bool IsProduction { get; private set; } diff --git a/src/NzbDrone.Common/EnvironmentInfo/StartupContext.cs b/src/NzbDrone.Common/EnvironmentInfo/StartupContext.cs index 3331b39e4..dbf4e6d65 100644 --- a/src/NzbDrone.Common/EnvironmentInfo/StartupContext.cs +++ b/src/NzbDrone.Common/EnvironmentInfo/StartupContext.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Common.EnvironmentInfo internal const string UNINSTALL_SERVICE = "u"; public const string HELP = "?"; public const string TERMINATE = "terminateexisting"; + public const string RESTART = "restart"; public StartupContext(params string[] args) { diff --git a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs index f82da6eb1..9e8f5dd1b 100644 --- a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs @@ -28,6 +28,7 @@ namespace NzbDrone.Core.Configuration string Password { get; } string LogLevel { get; } string Branch { get; } + bool AutoUpdate { get; } string ApiKey { get; } bool Torrent { get; } string SslCertHash { get; } @@ -133,6 +134,11 @@ namespace NzbDrone.Core.Configuration get { return GetValue("Branch", "master").ToLowerInvariant(); } } + public bool AutoUpdate + { + get { return GetValueBoolean("AutoUpdate", false, persist: false); } + } + public string Username { get { return GetValue("Username", ""); } diff --git a/src/NzbDrone.Core/Lifecycle/LifecycleService.cs b/src/NzbDrone.Core/Lifecycle/LifecycleService.cs index 8b2f3a21e..1d02490c6 100644 --- a/src/NzbDrone.Core/Lifecycle/LifecycleService.cs +++ b/src/NzbDrone.Core/Lifecycle/LifecycleService.cs @@ -48,22 +48,12 @@ namespace NzbDrone.Core.Lifecycle { _logger.Info("Restart requested."); - if (OsInfo.IsMono) - { - _processProvider.SpawnNewProcess(_runtimeInfo.ExecutingApplication, "--terminateexisting --nobrowser"); - } - _eventAggregator.PublishEvent(new ApplicationShutdownRequested(true)); if (_runtimeInfo.IsWindowsService) { _serviceProvider.Restart(ServiceProvider.NZBDRONE_SERVICE_NAME); } - - else - { - _processProvider.SpawnNewProcess(_runtimeInfo.ExecutingApplication, "--terminateexisting --nobrowser"); - } } } } diff --git a/src/NzbDrone.Host/ApplicationServer.cs b/src/NzbDrone.Host/ApplicationServer.cs index 1e6a5f713..c299c4870 100644 --- a/src/NzbDrone.Host/ApplicationServer.cs +++ b/src/NzbDrone.Host/ApplicationServer.cs @@ -55,7 +55,7 @@ namespace NzbDrone.Host { if (OsInfo.IsMono) { - Console.CancelKeyPress += (sender, eventArgs) => _processProvider.Kill(_processProvider.GetCurrentProcess().Id); + Console.CancelKeyPress += (sender, eventArgs) => LogManager.Configuration = null; } _runtimeInfo.IsRunning = true; @@ -90,13 +90,14 @@ namespace NzbDrone.Host public void Handle(ApplicationShutdownRequested message) { - if (OsInfo.IsMono) + if (!_runtimeInfo.IsWindowsService) { - _processProvider.Kill(_processProvider.GetCurrentProcess().Id); - } + if (message.Restarting) + { + _runtimeInfo.RestartPending = true; + } - if (!_runtimeInfo.IsWindowsService && !message.Restarting) - { + LogManager.Configuration = null; Shutdown(); } } diff --git a/src/NzbDrone.Host/Bootstrap.cs b/src/NzbDrone.Host/Bootstrap.cs index cac59c291..ca277851c 100644 --- a/src/NzbDrone.Host/Bootstrap.cs +++ b/src/NzbDrone.Host/Bootstrap.cs @@ -5,6 +5,7 @@ using NLog; using NzbDrone.Common.Composition; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation; +using NzbDrone.Common.Processes; using NzbDrone.Common.Security; using NzbDrone.Core.Datastore; @@ -59,6 +60,11 @@ namespace NzbDrone.Host { if (!IsInUtilityMode(applicationModes)) { + if (startupContext.Flags.Contains(StartupContext.RESTART)) + { + Thread.Sleep(2000); + } + EnsureSingleInstance(applicationModes == ApplicationModes.Service, startupContext); } @@ -73,12 +79,7 @@ namespace NzbDrone.Host return; } - var runTimeInfo = _container.Resolve(); - - while (runTimeInfo.IsRunning) - { - Thread.Sleep(1000); - } + _container.Resolve().Spin(); } private static void EnsureSingleInstance(bool isService, StartupContext startupContext) diff --git a/src/NzbDrone.Host/NzbDrone.Host.csproj b/src/NzbDrone.Host/NzbDrone.Host.csproj index 703828a2f..861ae81e9 100644 --- a/src/NzbDrone.Host/NzbDrone.Host.csproj +++ b/src/NzbDrone.Host/NzbDrone.Host.csproj @@ -101,6 +101,7 @@ + diff --git a/src/NzbDrone.Host/SpinService.cs b/src/NzbDrone.Host/SpinService.cs new file mode 100644 index 000000000..8b07dfbc2 --- /dev/null +++ b/src/NzbDrone.Host/SpinService.cs @@ -0,0 +1,36 @@ +using System.Threading; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; + +namespace NzbDrone.Host +{ + public interface IWaitForExit + { + void Spin(); + } + + public class SpinService : IWaitForExit + { + private readonly IRuntimeInfo _runtimeInfo; + private readonly IProcessProvider _processProvider; + + public SpinService(IRuntimeInfo runtimeInfo, IProcessProvider processProvider) + { + _runtimeInfo = runtimeInfo; + _processProvider = processProvider; + } + + public void Spin() + { + while (_runtimeInfo.IsRunning) + { + Thread.Sleep(1000); + } + + if (_runtimeInfo.RestartPending) + { + _processProvider.SpawnNewProcess(_runtimeInfo.ExecutingApplication, "--restart --nobrowser"); + } + } + } +} diff --git a/src/UI/Settings/General/GeneralViewTemplate.html b/src/UI/Settings/General/GeneralViewTemplate.html index 44d9e3d11..126283e4f 100644 --- a/src/UI/Settings/General/GeneralViewTemplate.html +++ b/src/UI/Settings/General/GeneralViewTemplate.html @@ -142,5 +142,28 @@ + + {{#if_mono}} +
+ + +
+
+ {{/if_mono}}
diff --git a/src/UpgradeLog.htm b/src/UpgradeLog.htm deleted file mode 100644 index 2732cdfdc8c588e9ef23d8894479e27f14c39aa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60810 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 M!-zpo!NQy7|Dbd882|tP diff --git a/src/UpgradeLog2.htm b/src/UpgradeLog2.htm deleted file mode 100644 index 2732cdfdc8c588e9ef23d8894479e27f14c39aa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60810 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 M!-zpo!NQy7|Dbd882|tP