diff --git a/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj b/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj index f70d3ab1..61e6e314 100644 --- a/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj +++ b/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj @@ -22,7 +22,7 @@ Microsoft.SqlServer.KustoServiceLayer.Tool - 1.0.0 + 1.1.0 .NET client Kusto Service application, usable as a dotnet tool. This package is intended to be used by internal applications only and should not be referenced directly. true $(AssemblyName) diff --git a/src/Microsoft.Kusto.ServiceLayer/Program.cs b/src/Microsoft.Kusto.ServiceLayer/Program.cs index bce3c03b..06bb7b90 100644 --- a/src/Microsoft.Kusto.ServiceLayer/Program.cs +++ b/src/Microsoft.Kusto.ServiceLayer/Program.cs @@ -48,6 +48,12 @@ namespace Microsoft.Kusto.ServiceLayer SqlToolsContext sqlToolsContext = new SqlToolsContext(hostDetails); ServiceHost serviceHost = HostLoader.CreateAndStartServiceHost(sqlToolsContext); + // If this service was started by another process, then it should shutdown when that parent process does. + if (commandOptions.ParentProcessId != null) + { + ProcessExitTimer.Start(commandOptions.ParentProcessId.Value); + } + serviceHost.WaitForExit(); } catch (Exception e) diff --git a/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs b/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs index a75ddf95..eebc6651 100644 --- a/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs +++ b/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs @@ -66,6 +66,13 @@ namespace Microsoft.SqlTools.Hosting.Utility case "-parallel-message-processing": ParallelMessageProcessing = true; break; + case "-parent-pid": + string nextArg = args[++i]; + if (Int32.TryParse(nextArg, out int parsedInt)) + { + ParentProcessId = parsedInt; + } + break; default: ErrorMessage += string.Format("Unknown argument \"{0}\"" + Environment.NewLine, argName); break; @@ -140,6 +147,12 @@ namespace Microsoft.SqlTools.Hosting.Utility /// public bool ParallelMessageProcessing { get; private set; } = false; + /// + /// The ID of the process that started this service. This is used to check when the parent + /// process exits so that the service process can exit at the same time. + /// + public int? ParentProcessId { get; private set; } + public virtual void SetLocale(string locale) { try diff --git a/src/Microsoft.SqlTools.Hosting/Utility/ProcessExitTimer.cs b/src/Microsoft.SqlTools.Hosting/Utility/ProcessExitTimer.cs new file mode 100644 index 00000000..ee29b552 --- /dev/null +++ b/src/Microsoft.SqlTools.Hosting/Utility/ProcessExitTimer.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Diagnostics; +using System.Threading; + +namespace Microsoft.SqlTools.Utility +{ + public static class ProcessExitTimer + { + /// + /// Starts a thread that checks if the provided parent process has exited each time the provided interval has elapsed. + /// Once the parent process has exited the process that started the timer also exits. + /// + /// The ID of the parent process to monitor. + /// The time interval in milliseconds for when to poll the parent process. + /// The ID of the thread running the timer. + public static int Start(int parentProcessId, int intervalMs = 10000) + { + var statusThread = new Thread(() => CheckParentStatusLoop(parentProcessId, intervalMs)); + statusThread.Start(); + return statusThread.ManagedThreadId; + } + + private static void CheckParentStatusLoop(int parentProcessId, int intervalMs) + { + var parent = Process.GetProcessById(parentProcessId); + Logger.Write(TraceEventType.Information, $"Starting thread to check status of parent process. Parent PID: {parent.Id}"); + while (true) + { + if (parent.HasExited) + { + var processName = Process.GetCurrentProcess().ProcessName; + Logger.Write(TraceEventType.Information, $"Terminating {processName} process because parent process has exited. Parent PID: {parent.Id}"); + Environment.Exit(0); + } + Thread.Sleep(intervalMs); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj index aaad9946..9aa9fc7b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj +++ b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj @@ -23,7 +23,7 @@ Microsoft.SqlServer.SqlToolsServiceLayer.Tool - 1.0.0 + 1.1.0 .NET client SQL Tools Service application, usable as a dotnet tool. This package is intended to be used by internal applications only and should not be referenced directly. true $(AssemblyName) diff --git a/src/Microsoft.SqlTools.ServiceLayer/Program.cs b/src/Microsoft.SqlTools.ServiceLayer/Program.cs index 3a0a6368..fcf0cfa7 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Program.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Program.cs @@ -9,7 +9,6 @@ using Microsoft.SqlTools.ServiceLayer.SqlContext; using Microsoft.SqlTools.ServiceLayer.Utility; using Microsoft.SqlTools.Utility; using System.Diagnostics; -using System.Threading; namespace Microsoft.SqlTools.ServiceLayer { @@ -57,9 +56,7 @@ namespace Microsoft.SqlTools.ServiceLayer // If this service was started by another process, then it should shutdown when that parent process does. if (commandOptions.ParentProcessId != null) { - var parentProcess = Process.GetProcessById(commandOptions.ParentProcessId.Value); - var statusThread = new Thread(() => CheckParentStatusLoop(parentProcess)); - statusThread.Start(); + ProcessExitTimer.Start(commandOptions.ParentProcessId.Value); } serviceHost.WaitForExit(); @@ -83,20 +80,5 @@ namespace Microsoft.SqlTools.ServiceLayer sqlClientListener?.Dispose(); } } - - private static void CheckParentStatusLoop(Process parent) - { - Logger.Write(TraceEventType.Information, $"Starting thread to check status of parent process. Parent PID: {parent.Id}"); - while (true) - { - if (parent.HasExited) - { - var processName = Process.GetCurrentProcess().ProcessName; - Logger.Write(TraceEventType.Information, $"Terminating {processName} process because parent process has exited. Parent PID: {parent.Id}"); - Environment.Exit(0); - } - Thread.Sleep(5000); - } - } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Utility/ServiceLayerCommandOptions.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/ServiceLayerCommandOptions.cs index 25d0a76d..d85d338e 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Utility/ServiceLayerCommandOptions.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/ServiceLayerCommandOptions.cs @@ -14,7 +14,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility { internal const string ServiceLayerServiceName = "MicrosoftSqlToolsServiceLayer.exe"; - private static readonly string[] serviceLayerCommandArgs = { "-d", "--developers", "--parent-pid" }; + private static readonly string[] serviceLayerCommandArgs = { "-d", "--developers" }; /** * List of contributors to this project, used as part of the onboarding process. @@ -24,8 +24,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility "Charles-Gagnon" }; - public int? ParentProcessId { get; private set; } - public ServiceLayerCommandOptions(string[] args) : base(args.Where(arg => !serviceLayerCommandArgs.Contains(arg)).ToArray(), ServiceLayerServiceName) { for (int i = 0; i < args.Length; ++i) @@ -44,13 +42,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility Console.WriteLine(string.Join(Environment.NewLine, contributors.Select(contributor => $"\t{contributor}"))); this.ShouldExit = true; break; - case "--parent-pid": - string nextArg = args[++i]; - if (Int32.TryParse(nextArg, out int parsedInt)) - { - ParentProcessId = parsedInt; - } - break; } } }