using System.IO;
///
/// Run the specified command as an external program. This method will return once the GVFSLock has been acquired.
///
/// The ID of the process that acquired the lock.
/// that can be signaled to exit the lock acquisition program.
private static ManualResetEventSlim RunCommandWithWaitAndStdIn(
GVFSFunctionalTestEnlistment enlistment,
int resetTimeout,
string pathToCommand,
string args,
string lockingProcessCommandName,
string stdinToQuit,
out int processId)
{
ManualResetEventSlim resetEvent = new ManualResetEventSlim(initialState: false);
ProcessStartInfo processInfo = new ProcessStartInfo(pathToCommand);
processInfo.WorkingDirectory = enlistment.RepoRoot;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardOutput = true;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardInput = true;
processInfo.Arguments = args;
Process holdingProcess = Process.Start(processInfo);
StreamWriter stdin = holdingProcess.StandardInput;
processId = holdingProcess.Id;
enlistment.WaitForLock(lockingProcessCommandName);
Task.Run(
() =>
{
resetEvent.Wait(resetTimeout);
try
{
// Make sure to let the holding process end.
if (stdin != null)
{
stdin.WriteLine(stdinToQuit);
stdin.Close();
}
if (holdingProcess != null)
{
bool holdingProcessHasExited = holdingProcess.WaitForExit(10000);
if (!holdingProcess.HasExited)
{
holdingProcess.Kill();
}
holdingProcess.Dispose();
holdingProcessHasExited.ShouldBeTrue("Locking process did not exit in time.");
}
}
catch (Exception ex)
{
Assert.Fail($"{nameof(RunCommandWithWaitAndStdIn)} exception closing stdin {ex.ToString()}");
}
finally
{
resetEvent.Set();
}
});
return resetEvent;
}
using System.IO;
protected virtual Result InvokeGitImpl(
string command,
string workingDirectory,
string dotGitDirectory,
bool useReadObjectHook,
Action writeStdIn,
Action parseStdOutLine,
int timeoutMs,
string gitObjectsDirectory = null)
{
if (failedToSetEncoding && writeStdIn != null)
{
return new Result(string.Empty, "Attempting to use to stdin, but the process does not have the right input encodings set.", Result.GenericFailureCode);
}
try
{
// From https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx
// To avoid deadlocks, use asynchronous read operations on at least one of the streams.
// Do not perform a synchronous read to the end of both redirected streams.
using (this.executingProcess = this.GetGitProcess(command, workingDirectory, dotGitDirectory, useReadObjectHook, redirectStandardError: true, gitObjectsDirectory: gitObjectsDirectory))
{
StringBuilder output = new StringBuilder();
StringBuilder errors = new StringBuilder();
this.executingProcess.ErrorDataReceived += (sender, args) =>
{
if (args.Data != null)
{
errors.Append(args.Data + "\n");
}
};
this.executingProcess.OutputDataReceived += (sender, args) =>
{
if (args.Data != null)
{
if (parseStdOutLine != null)
{
parseStdOutLine(args.Data);
}
else
{
output.Append(args.Data + "\n");
}
}
};
lock (this.executionLock)
{
lock (this.processLock)
{
if (this.stopping)
{
return new Result(string.Empty, nameof(GitProcess) + " is stopping", Result.GenericFailureCode);
}
this.executingProcess.Start();
if (this.LowerPriority)
{
try
{
this.executingProcess.PriorityClass = ProcessPriorityClass.BelowNormal;
}
catch (InvalidOperationException)
{
// This is thrown if the process completes before we can set its priority.
}
}
}
writeStdIn?.Invoke(this.executingProcess.StandardInput);
this.executingProcess.StandardInput.Close();
this.executingProcess.BeginOutputReadLine();
this.executingProcess.BeginErrorReadLine();
if (!this.executingProcess.WaitForExit(timeoutMs))
{
this.executingProcess.Kill();
return new Result(output.ToString(), "Operation timed out: " + errors.ToString(), Result.GenericFailureCode);
}
}
return new Result(output.ToString(), errors.ToString(), this.executingProcess.ExitCode);
}
}
catch (Win32Exception e)
{
return new Result(string.Empty, e.Message, Result.GenericFailureCode);
}
finally
{
this.executingProcess = null;
}
}