mirror of https://github.com/winsw/winsw
[JENKINS-42744] - Decouple the process start logic to a separate method in the helper class
parent
dd02dc8ef5
commit
615519f6a3
|
@ -133,25 +133,6 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a thread that protects the execution with a try/catch block.
|
||||
/// It appears that in .NET, unhandled exception in any thread causes the app to terminate
|
||||
/// http://msdn.microsoft.com/en-us/library/ms228965.aspx
|
||||
/// </summary>
|
||||
private void StartThread(ThreadStart main)
|
||||
{
|
||||
new Thread(delegate() {
|
||||
try
|
||||
{
|
||||
main();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Thread failed unexpectedly",e);
|
||||
}
|
||||
}).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the creation of the logfiles based on the optional logmode setting.
|
||||
/// </summary>
|
||||
|
@ -419,53 +400,26 @@ namespace winsw
|
|||
|
||||
private void StartProcess(Process processToStart, string arguments, String executable)
|
||||
{
|
||||
var ps = processToStart.StartInfo;
|
||||
ps.FileName = executable;
|
||||
ps.Arguments = arguments;
|
||||
ps.WorkingDirectory = _descriptor.WorkingDirectory;
|
||||
ps.CreateNoWindow = false;
|
||||
ps.UseShellExecute = false;
|
||||
ps.RedirectStandardInput = true; // this creates a pipe for stdin to the new process, instead of having it inherit our stdin.
|
||||
ps.RedirectStandardOutput = true;
|
||||
ps.RedirectStandardError = true;
|
||||
string msg = processToStart.Id + " - " + processToStart.StartInfo.FileName + " " + processToStart.StartInfo.Arguments;
|
||||
|
||||
foreach (string key in _envs.Keys)
|
||||
// Define handler of the completed process
|
||||
ProcessCompletionCallback processCompletionCallback = delegate(Process proc)
|
||||
{
|
||||
Environment.SetEnvironmentVariable(key, _envs[key]);
|
||||
// ps.EnvironmentVariables[key] = envs[key]; // bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163)
|
||||
}
|
||||
|
||||
// TODO: Make it generic via extension points. The issue mentioned above should be ideally worked around somehow
|
||||
ps.EnvironmentVariables[WinSWSystem.ENVVAR_NAME_SERVICE_ID.ToLower()] = _descriptor.Id;
|
||||
|
||||
processToStart.Start();
|
||||
Log.Info("Started " + processToStart.Id);
|
||||
|
||||
var priority = _descriptor.Priority;
|
||||
if (priority != ProcessPriorityClass.Normal)
|
||||
processToStart.PriorityClass = priority;
|
||||
|
||||
// monitor the completion of the process
|
||||
StartThread(delegate
|
||||
{
|
||||
string msg = processToStart.Id + " - " + processToStart.StartInfo.FileName + " " + processToStart.StartInfo.Arguments;
|
||||
processToStart.WaitForExit();
|
||||
|
||||
try
|
||||
{
|
||||
if (_orderlyShutdown)
|
||||
{
|
||||
LogEvent("Child process [" + msg + "] terminated with " + processToStart.ExitCode, EventLogEntryType.Information);
|
||||
LogEvent("Child process [" + msg + "] terminated with " + proc.ExitCode, EventLogEntryType.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogEvent("Child process [" + msg + "] finished with " + processToStart.ExitCode, EventLogEntryType.Warning);
|
||||
LogEvent("Child process [" + msg + "] finished with " + proc.ExitCode, EventLogEntryType.Warning);
|
||||
// if we finished orderly, report that to SCM.
|
||||
// by not reporting unclean shutdown, we let Windows SCM to decide if it wants to
|
||||
// restart the service automatically
|
||||
if (processToStart.ExitCode == 0)
|
||||
if (proc.ExitCode == 0)
|
||||
SignalShutdownComplete();
|
||||
Environment.Exit(processToStart.ExitCode);
|
||||
Environment.Exit(proc.ExitCode);
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException ioe)
|
||||
|
@ -475,13 +429,23 @@ namespace winsw
|
|||
|
||||
try
|
||||
{
|
||||
processToStart.Dispose();
|
||||
proc.Dispose();
|
||||
}
|
||||
catch (InvalidOperationException ioe)
|
||||
{
|
||||
LogEvent("Dispose " + ioe.Message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Invoke process and exit
|
||||
ProcessHelper.StartProcessAndCallbackForExit(
|
||||
processToStart: processToStart,
|
||||
executable: executable,
|
||||
arguments: arguments,
|
||||
envVars: _envs,
|
||||
workingDirectory: _descriptor.WorkingDirectory,
|
||||
priority: _descriptor.Priority,
|
||||
callback: processCompletionCallback);
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Management;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace winsw.Util
|
||||
{
|
||||
|
@ -117,6 +118,76 @@ namespace winsw.Util
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Also move StartProcess methods once LogEvent()/WriteEvent() mess gets solved
|
||||
//TODO: generalize API
|
||||
/// <summary>
|
||||
/// Starts a process and asynchronosly waits for its termination.
|
||||
/// Once the process exits, the callback will be invoked.
|
||||
/// </summary>
|
||||
/// <param name="processToStart">Process object to be used</param>
|
||||
/// <param name="arguments">Arguments to be passed</param>
|
||||
/// <param name="executable">Executable, which should be invoked</param>
|
||||
/// <param name="envVars">Additional environment variables</param>
|
||||
/// <param name="workingDirectory">Working directory</param>
|
||||
/// <param name="priority">Priority</param>
|
||||
/// <param name="callback">Completion callback</param>
|
||||
public static void StartProcessAndCallbackForExit(Process processToStart, String executable, string arguments, Dictionary<string, string> envVars,
|
||||
string workingDirectory, ProcessPriorityClass priority, ProcessCompletionCallback callback)
|
||||
{
|
||||
var ps = processToStart.StartInfo;
|
||||
ps.FileName = executable;
|
||||
ps.Arguments = arguments;
|
||||
ps.WorkingDirectory = workingDirectory;
|
||||
ps.CreateNoWindow = false;
|
||||
ps.UseShellExecute = false;
|
||||
ps.RedirectStandardInput = true; // this creates a pipe for stdin to the new process, instead of having it inherit our stdin.
|
||||
ps.RedirectStandardOutput = true;
|
||||
ps.RedirectStandardError = true;
|
||||
|
||||
foreach (string key in envVars.Keys)
|
||||
{
|
||||
Environment.SetEnvironmentVariable(key, envVars[key]);
|
||||
// ps.EnvironmentVariables[key] = envs[key]; // bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163)
|
||||
}
|
||||
|
||||
//TODO: move outside, stubbed to reproduce the issue
|
||||
// TODO: Make it generic via extension points. The issue mentioned above should be ideally worked around somehow
|
||||
ps.EnvironmentVariables[WinSWSystem.ENVVAR_NAME_SERVICE_ID.ToLower()] = "myapp";// _descriptor.Id;
|
||||
// Environment.SetEnvironmentVariable(WinSWSystem.ENVVAR_NAME_SERVICE_ID.ToLower(), _descriptor.Id);
|
||||
|
||||
processToStart.Start();
|
||||
Logger.Info("Started process " + processToStart.Id);
|
||||
|
||||
if (priority != ProcessPriorityClass.Normal)
|
||||
processToStart.PriorityClass = priority;
|
||||
|
||||
// monitor the completion of the process
|
||||
StartThread(delegate
|
||||
{
|
||||
processToStart.WaitForExit();
|
||||
callback(processToStart);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a thread that protects the execution with a try/catch block.
|
||||
/// It appears that in .NET, unhandled exception in any thread causes the app to terminate
|
||||
/// http://msdn.microsoft.com/en-us/library/ms228965.aspx
|
||||
/// </summary>
|
||||
public static void StartThread(ThreadStart main)
|
||||
{
|
||||
new Thread(delegate()
|
||||
{
|
||||
try
|
||||
{
|
||||
main();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error("Thread failed unexpectedly", e);
|
||||
}
|
||||
}).Start();
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void ProcessCompletionCallback(Process process);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue