public void WaitForExitPollsAllowsExecutableToContinueAfterTimeoutIfCpuActivity() { // Arrange var idleTimeout = TimeSpan.MinValue; var tracer = new Mock<ITracer>(MockBehavior.Strict); var idleManager = new IdleManager(idleTimeout, tracer.Object); var process = new Mock<IProcess>(MockBehavior.Strict); // Setup int num = 10, cpu = 0; process.SetupGet(f => f.Name) .Returns("Test-Process"); process.Setup(f => f.WaitForExit(It.IsAny<TimeSpan>())) .Returns(() => --num == 0); process.Setup(f => f.GetTotalProcessorTime(It.IsAny<ITracer>())) .Returns(() => TimeSpan.FromSeconds(++cpu)) .Verifiable(); process.Setup(f => f.WaitUntilEOF()) .Verifiable(); tracer.Setup(t => t.Trace(It.IsAny<string>(), It.IsAny<IDictionary<string, string>>())) .Verifiable(); // Act idleManager.WaitForExit(process.Object); // Assert process.Verify(); Assert.Equal(0, num); }
public CommandResult ExecuteCommand(string command, string workingDirectory) { var idleManager = new IdleManager(_settings.GetCommandIdleTimeout(), _tracer); var result = new CommandResult(); int exitCode = 0; var outputBuilder = new StringBuilder(); var errorBuilder = new StringBuilder(); Action<CommandEvent> handler = args => { idleManager.UpdateActivity(); switch (args.EventType) { case CommandEventType.Output: outputBuilder.AppendLine(args.Data); break; case CommandEventType.Error: errorBuilder.AppendLine(args.Data); break; case CommandEventType.Complete: exitCode = args.ExitCode; break; default: break; } }; try { // Code reuse is good CommandEvent += handler; ExecuteCommandAsync(command, workingDirectory); } finally { CommandEvent -= handler; } idleManager.WaitForExit(_executingProcess); result.Output = outputBuilder.ToString(); result.Error = errorBuilder.ToString(); result.ExitCode = exitCode; return result; }
public void WaitForExitWaitsForEOFPriorToExiting() { // Arrange var idleTimeout = DeploymentSettingsExtension.DefaultCommandIdleTimeout; var tracer = new Mock<ITracer>(MockBehavior.Strict); var idleManager = new IdleManager(idleTimeout, tracer.Object); var process = new Mock<IProcess>(MockBehavior.Strict); // Setup process.SetupGet(f => f.Name) .Returns("Test-Process"); process.Setup(f => f.WaitForExit(It.IsAny<TimeSpan>())) .Returns(true) .Verifiable(); process.Setup(f => f.WaitUntilEOF()) .Verifiable(); // Act idleManager.WaitForExit(process.Object); // Assert process.Verify(); }
public void WaitForExitPollsAllowsExecutableToContinueAfterTimeoutIfIOActivity() { // Arrange var idleTimeout = TimeSpan.MinValue; var tracer = new Mock<ITracer>(MockBehavior.Strict); var idleManager = new IdleManager(idleTimeout, tracer.Object); var process = new Mock<IProcess>(MockBehavior.Strict); // Setup int num = 10; process.SetupGet(f => f.Name) .Returns("Test-Process"); process.Setup(f => f.WaitForExit(It.IsAny<TimeSpan>())) .Returns(() => { if (--num == 0) { return true; } else { Thread.Sleep(10); idleManager.UpdateActivity(); return false; } }); process.Setup(f => f.WaitUntilEOF()) .Verifiable(); // Act idleManager.WaitForExit(process.Object); // Assert process.Verify(); Assert.Equal(0, num); }
private static async Task CopyStreamAsync(Stream from, Stream to, IdleManager idleManager, CancellationToken cancellationToken, bool closeAfterCopy = false) { try { byte[] bytes = new byte[1024]; int read = 0; while ((read = await from.ReadAsync(bytes, 0, bytes.Length, cancellationToken)) != 0) { idleManager.UpdateActivity(); await to.WriteAsync(bytes, 0, read, cancellationToken); } idleManager.UpdateActivity(); } finally { // this is needed specifically for input stream // in order to tell executable that the input is done if (closeAfterCopy) { to.Close(); } } }
// This is pure async process execution public async Task<int> ExecuteAsync(ITracer tracer, string arguments, Stream output, Stream error, Stream input = null, IdleManager idleManager = null) { using (GetProcessStep(tracer, arguments)) { using (Process process = CreateProcess(arguments)) { var wrapper = new ProcessWrapper(process); int exitCode = await wrapper.Start(output, error, input, idleManager ?? new IdleManager(IdleTimeout, tracer)); tracer.TraceProcessExitCode(process); return exitCode; } } }
private Tuple<string, string> ExecuteInternal(ITracer tracer, Func<string, bool> onWriteOutput, Func<string, bool> onWriteError, Encoding encoding, string arguments, params object[] args) { var cmdArguments = String.Format(arguments, args); var errorBuffer = new StringBuilder(); var outputBuffer = new StringBuilder(); var idleManager = new IdleManager(IdleTimeout, tracer); var outputStream = new AsyncStreamWriter(data => { idleManager.UpdateActivity(); if (data != null) { if (onWriteOutput(data)) { outputBuffer.AppendLine(Encoding.UTF8.GetString(encoding.GetBytes(data))); } } }, Encoding ?? Console.OutputEncoding); var errorStream = new AsyncStreamWriter(data => { idleManager.UpdateActivity(); if (data != null) { if (onWriteError(data)) { errorBuffer.AppendLine(Encoding.UTF8.GetString(encoding.GetBytes(data))); } } }, Encoding ?? Console.OutputEncoding); int exitCode; try { // common execute exitCode = Task.Run(() => ExecuteAsync(tracer, cmdArguments, outputStream, errorStream, idleManager: idleManager)).Result; } catch (AggregateException ex) { foreach (var inner in ex.Flatten().InnerExceptions) { onWriteError(inner.Message); } throw; } catch (Exception ex) { onWriteError(ex.Message); throw; } finally { // flush out last buffer if any outputStream.Dispose(); errorStream.Dispose(); } string output = outputBuffer.ToString().Trim(); string error = errorBuffer.ToString().Trim(); if (exitCode != 0) { throw new CommandLineException(Path, cmdArguments, !String.IsNullOrEmpty(error) ? error : output) { ExitCode = exitCode, Output = output, Error = error }; } return Tuple.Create(output, error); }
public Tuple <string, string> Execute(ITracer tracer, Func <string, bool> onWriteOutput, Func <string, bool> onWriteError, Encoding encoding, string arguments, params object[] args) { using (GetProcessStep(tracer, arguments, args)) { Process process = CreateProcess(arguments, args); process.EnableRaisingEvents = true; var errorBuffer = new StringBuilder(); var outputBuffer = new StringBuilder(); var idleManager = new IdleManager(IdleTimeout, tracer); process.OutputDataReceived += (sender, e) => { idleManager.UpdateActivity(); if (e.Data != null) { if (onWriteOutput(e.Data)) { outputBuffer.AppendLine(Encoding.UTF8.GetString(encoding.GetBytes(e.Data))); } } }; process.ErrorDataReceived += (sender, e) => { idleManager.UpdateActivity(); if (e.Data != null) { if (onWriteError(e.Data)) { errorBuffer.AppendLine(Encoding.UTF8.GetString(encoding.GetBytes(e.Data))); } } }; process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); try { idleManager.WaitForExit(process); } catch (Exception ex) { onWriteError(ex.Message); throw; } tracer.TraceProcessExitCode(process); string output = outputBuffer.ToString().Trim(); string error = errorBuffer.ToString().Trim(); if (process.ExitCode != 0) { string text = String.IsNullOrEmpty(error) ? output : error; throw new CommandLineException(Path, process.StartInfo.Arguments, text) { ExitCode = process.ExitCode, Output = output, Error = error }; } return(Tuple.Create(output, error)); } }
private Tuple <string, string> ExecuteInternal(ITracer tracer, Func <string, bool> onWriteOutput, Func <string, bool> onWriteError, Encoding encoding, string arguments, params object[] args) { var cmdArguments = String.Format(arguments, args); var errorBuffer = new StringBuilder(); var outputBuffer = new StringBuilder(); var idleManager = new IdleManager(IdleTimeout, tracer); var outputStream = new AsyncStreamWriter(data => { idleManager.UpdateActivity(); if (data != null) { if (onWriteOutput(data)) { outputBuffer.AppendLine(Encoding.UTF8.GetString(encoding.GetBytes(data))); } } }, Encoding ?? Console.OutputEncoding); var errorStream = new AsyncStreamWriter(data => { idleManager.UpdateActivity(); if (data != null) { if (onWriteError(data)) { errorBuffer.AppendLine(Encoding.UTF8.GetString(encoding.GetBytes(data))); } } }, Encoding ?? Console.OutputEncoding); int exitCode; try { // common execute exitCode = Task.Run(() => ExecuteAsync(tracer, cmdArguments, outputStream, errorStream, idleManager: idleManager)).Result; } catch (AggregateException ex) { foreach (var inner in ex.Flatten().InnerExceptions) { onWriteError(inner.Message); } throw; } catch (Exception ex) { onWriteError(ex.Message); throw; } finally { // flush out last buffer if any outputStream.Dispose(); errorStream.Dispose(); } string output = outputBuffer.ToString().Trim(); string error = errorBuffer.ToString().Trim(); if (exitCode != 0) { throw new CommandLineException(Path, cmdArguments, error) { ExitCode = exitCode, Output = output, Error = error }; } return(Tuple.Create(output, error)); }
public async Task ProcesStartBasicTests() { var tracer = Mock.Of<ITracer>(); var process = new Mock<IProcess>(); var idleManager = new IdleManager(TimeSpan.MaxValue, tracer); var expectedExitCode = 10; var input = "this is input"; var output = "this is output"; var error = "this is error"; var inputBuffer = new byte[1024]; var actualOutput = new MemoryStream(); var actualError = new MemoryStream(); var actualInput = new MemoryStream(inputBuffer); var expectedInput = new MemoryStream(); var bytes = Encoding.UTF8.GetBytes(input); expectedInput.Write(bytes, 0, bytes.Length); expectedInput.Position = 0; var expectedOutput = new MemoryStream(); bytes = Encoding.UTF8.GetBytes(output); expectedOutput.Write(bytes, 0, bytes.Length); expectedOutput.Position = 0; var expectedError = new MemoryStream(); bytes = Encoding.UTF8.GetBytes(error); expectedError.Write(bytes, 0, bytes.Length); expectedError.Position = 0; // Setup process.SetupGet(p => p.StandardInput) .Returns(new StreamWriter(actualInput)); process.SetupGet(p => p.StandardOutput) .Returns(new StreamReader(expectedOutput)); process.SetupGet(p => p.StandardError) .Returns(new StreamReader(expectedError)); process.SetupGet(p => p.ExitCode) .Returns(expectedExitCode); process.Setup(p => p.WaitForExit(It.IsAny<TimeSpan>())) .Returns(true); // Test int actualExitCode = await process.Object.Start(tracer, actualOutput, actualError, expectedInput, idleManager); // Assert Assert.Equal(expectedExitCode, actualExitCode); Assert.Throws<ObjectDisposedException>(() => actualInput.Length); Assert.Equal(input, Encoding.UTF8.GetString(inputBuffer, 0, input.Length)); Assert.Equal(output, Encoding.UTF8.GetString(actualOutput.GetBuffer(), 0, (int)actualOutput.Length)); Assert.Equal(error, Encoding.UTF8.GetString(actualError.GetBuffer(), 0, (int)actualError.Length)); }
public static async Task <int> Start(this IProcess process, ITracer tracer, Stream output, Stream error, Stream input = null, IdleManager idleManager = null) { var cancellationTokenSource = new CancellationTokenSource(); process.Start(); var tasks = new List <Task>(); if (input != null) { tasks.Add(CopyStreamAsync(input, process.StandardInput.BaseStream, idleManager, cancellationTokenSource.Token, closeAfterCopy: true)); } tasks.Add(CopyStreamAsync(process.StandardOutput.BaseStream, output, idleManager, cancellationTokenSource.Token)); tasks.Add(CopyStreamAsync(process.StandardError.BaseStream, error, idleManager, cancellationTokenSource.Token)); idleManager.WaitForExit(process); // Process has exited, draining the stdout and stderr await FlushAllAsync(process, tracer, idleManager, cancellationTokenSource, tasks); return(process.ExitCode); }
public void WaitForExitPollsKillsProcessIfProcessorTimeDoesNotChangeAndNotUpdated() { // Arrange var tracer = new Mock<ITracer>(MockBehavior.Strict); DateTime startTime = DateTime.UtcNow; TimeSpan idleTimeout = TimeSpan.FromMilliseconds(100); var idleManager = new IdleManager(idleTimeout, tracer.Object); var process = new Mock<IProcess>(MockBehavior.Strict); // Setup process.SetupGet(f => f.Name) .Returns("Test-Process"); process.SetupGet(f => f.Arguments) .Returns(""); process.Setup(f => f.WaitForExit(It.IsAny<TimeSpan>())) .Returns(() => { Thread.Sleep(10); return false; }); process.Setup(f => f.GetTotalProcessorTime(It.IsAny<ITracer>())) .Returns(TimeSpan.Zero); process.Setup(f => f.Kill(tracer.Object)) .Verifiable(); tracer.Setup(t => t.Trace(It.IsAny<string>(), It.IsAny<IDictionary<string, string>>())) .Verifiable(); // Act var ex = Assert.Throws<CommandLineException>(() => idleManager.WaitForExit(process.Object)); // Assert process.Verify(); Assert.True(DateTime.UtcNow - startTime >= idleTimeout); Assert.Contains("Command 'Test-Process ' aborted due to no output and CPU activity for", ex.Message); }
// This is pure async process execution public async Task <int> ExecuteAsync(ITracer tracer, string arguments, Stream output, Stream error, Stream input = null, IdleManager idleManager = null) { using (GetProcessStep(tracer, arguments)) { using (Process process = CreateProcess(arguments)) { var wrapper = new ProcessWrapper(process); int exitCode = await wrapper.Start(tracer, output, error, input, idleManager ?? new IdleManager(IdleTimeout, tracer)); tracer.TraceProcessExitCode(process); return(exitCode); } } }
// this is used exclusive in git sever scenario public void Execute(ITracer tracer, Stream input, Stream output, string arguments, params object[] args) { var cmdArguments = String.Format(arguments, args); var errorStream = new MemoryStream(); var idleManager = new IdleManager(IdleTimeout, tracer, output); // common execute int exitCode = Task.Run(() => ExecuteAsync(tracer, cmdArguments, output, errorStream, input, idleManager)).Result; string error = GetString(errorStream); if (exitCode != 0) { throw new CommandLineException(Path, cmdArguments, error) { ExitCode = exitCode, Error = error }; } }
public static async Task<int> Start(this IProcess process, ITracer tracer, Stream output, Stream error, Stream input = null, IdleManager idleManager = null) { var cancellationTokenSource = new CancellationTokenSource(); process.Start(); var tasks = new List<Task>(); if (input != null) { tasks.Add(CopyStreamAsync(input, process.StandardInput.BaseStream, idleManager, cancellationTokenSource.Token, closeAfterCopy: true)); } tasks.Add(CopyStreamAsync(process.StandardOutput.BaseStream, output, idleManager, cancellationTokenSource.Token)); tasks.Add(CopyStreamAsync(process.StandardError.BaseStream, error, idleManager, cancellationTokenSource.Token)); idleManager.WaitForExit(process); // Process has exited, draining the stdout and stderr await FlushAllAsync(process, tracer, idleManager, cancellationTokenSource, tasks); return process.ExitCode; }
public void Execute(ITracer tracer, Stream input, Stream output, string arguments, params object[] args) { using (GetProcessStep(tracer, arguments, args)) { var process = CreateProcess(arguments, args); process.Start(); var idleManager = new IdleManager(Path, IdleTimeout, tracer); Func<StreamReader, string> reader = (StreamReader streamReader) => streamReader.ReadToEnd(); Action<Stream, Stream, bool> copyStream = (Stream from, Stream to, bool closeAfterCopy) => { try { byte[] bytes = new byte[1024]; int read = 0; while ((read = from.Read(bytes, 0, bytes.Length)) != 0) { idleManager.UpdateActivity(); to.Write(bytes, 0, read); } idleManager.UpdateActivity(); if (closeAfterCopy) { to.Close(); } } catch (Exception ex) { tracer.TraceError(ex); } }; IAsyncResult errorReader = reader.BeginInvoke(process.StandardError, null, null); IAsyncResult inputResult = null; if (input != null) { // Copy into the input stream, and close it to tell the exe it can process it inputResult = copyStream.BeginInvoke(input, process.StandardInput.BaseStream, true, null, null); } // Copy the exe's output into the output stream IAsyncResult outputResult = copyStream.BeginInvoke(process.StandardOutput.BaseStream, output, false, null, null); idleManager.WaitForExit(process); // Wait for the input operation to complete if (inputResult != null) { inputResult.AsyncWaitHandle.WaitOne(); } // Wait for the output operation to be complete outputResult.AsyncWaitHandle.WaitOne(); string error = reader.EndInvoke(errorReader); tracer.Trace("Process dump", new Dictionary<string, string> { { "exitCode", process.ExitCode.ToString() }, { "type", "processOutput" } }); if (process.ExitCode != 0) { throw new CommandLineException(error) { ExitCode = process.ExitCode, Error = error }; } } }
private static async Task FlushAllAsync(IProcess process, ITracer tracer, IdleManager idleManager, CancellationTokenSource cancellationTokenSource, IEnumerable<Task> tasks) { var prevActivity = DateTime.MinValue; while (true) { // Wait for either delay or io tasks var delay = Task.Delay(StandardOutputDrainTimeout, cancellationTokenSource.Token); var stdio = Task.WhenAll(tasks); var completed = await Task.WhenAny(stdio, delay); // if delay commpleted first (meaning timeout), check if activity and continue to wait if (completed == delay) { var lastActivity = idleManager.LastActivity; if (lastActivity != prevActivity) { prevActivity = lastActivity; continue; } } // clean up all pending tasks by cancelling them // this is important so we don't have runaway tasks cancellationTokenSource.Cancel(); // in case of stdoutput/err have no activity within given time // we force close all streams if (completed == delay) { // TODO, suwatch: MDS Kudu SiteExtension using (tracer.Step("Flush stdio and stderr have no activity within given time")) { bool exited = process.HasExited; SafeCloseStream(process.StandardOutput.BaseStream); SafeCloseStream(process.StandardError.BaseStream); // this means no activity within given time // and process has not exited if (!exited) { throw new TimeoutException("Timeout draining standard input, output and error!"); } } } // happy path break; } }
public Tuple<string, string> Execute(ITracer tracer, Func<string, bool> onWriteOutput, Func<string, bool> onWriteError, Encoding encoding, string arguments, params object[] args) { using (GetProcessStep(tracer, arguments, args)) { Process process = CreateProcess(arguments, args); process.EnableRaisingEvents = true; var errorBuffer = new StringBuilder(); var outputBuffer = new StringBuilder(); var idleManager = new IdleManager(Path, IdleTimeout, tracer); process.OutputDataReceived += (sender, e) => { idleManager.UpdateActivity(); if (e.Data != null) { if (onWriteOutput(e.Data)) { outputBuffer.AppendLine(Encoding.UTF8.GetString(encoding.GetBytes(e.Data))); } } }; process.ErrorDataReceived += (sender, e) => { idleManager.UpdateActivity(); if (e.Data != null) { if (onWriteError(e.Data)) { errorBuffer.AppendLine(Encoding.UTF8.GetString(encoding.GetBytes(e.Data))); } } }; process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); try { idleManager.WaitForExit(process); } catch (Exception ex) { onWriteError(ex.Message); throw; } tracer.Trace("Process dump", new Dictionary<string, string> { { "exitCode", process.ExitCode.ToString() }, { "type", "processOutput" } }); string output = outputBuffer.ToString().Trim(); string error = errorBuffer.ToString().Trim(); if (process.ExitCode != 0) { string text = String.IsNullOrEmpty(error) ? output : error; throw new CommandLineException(text) { ExitCode = process.ExitCode, Output = output, Error = error }; } return Tuple.Create(output, error); } }
public Tuple<string, string> Execute(string arguments, params object[] args) #endif { #if !SITEMANAGEMENT using (GetProcessStep(tracer, arguments, args)) { #endif var process = CreateProcess(arguments, args); process.Start(); #if !SITEMANAGEMENT var idleManager = new IdleManager(Path, IdleTimeout, tracer); #else var idleManager = new IdleManager(); #endif Func<StreamReader, string> reader = (StreamReader streamReader) => { var strb = new StringBuilder(); char[] buffer = new char[1024]; int read; while ((read = streamReader.ReadBlock(buffer, 0, buffer.Length)) != 0) { idleManager.UpdateActivity(); strb.Append(buffer, 0, read); } idleManager.UpdateActivity(); return strb.ToString(); }; IAsyncResult outputReader = reader.BeginInvoke(process.StandardOutput, null, null); IAsyncResult errorReader = reader.BeginInvoke(process.StandardError, null, null); process.StandardInput.Close(); idleManager.WaitForExit(process); string output = reader.EndInvoke(outputReader); string error = reader.EndInvoke(errorReader); #if !SITEMANAGEMENT tracer.Trace("Process dump", new Dictionary<string, string> { { "exitCode", process.ExitCode.ToString() }, { "type", "processOutput" } }); #endif // Sometimes, we get an exit code of 1 even when the command succeeds (e.g. with 'git reset .'). // So also make sure there is an error string if (process.ExitCode != 0) { string text = String.IsNullOrEmpty(error) ? output : error; throw new CommandLineException(text) { ExitCode = process.ExitCode, Output = output, Error = error }; } return Tuple.Create(output, error); #if !SITEMANAGEMENT } #endif }
public void Execute(ITracer tracer, Stream input, Stream output, string arguments, params object[] args) { using (GetProcessStep(tracer, arguments, args)) { var process = CreateProcess(arguments, args); process.Start(); var idleManager = new IdleManager(IdleTimeout, tracer, output); Func <StreamReader, string> reader = (StreamReader streamReader) => streamReader.ReadToEnd(); Action <Stream, Stream, bool> copyStream = (Stream from, Stream to, bool closeAfterCopy) => { try { byte[] bytes = new byte[1024]; int read = 0; bool writeError = false; while ((read = from.Read(bytes, 0, bytes.Length)) != 0) { idleManager.UpdateActivity(); try { if (!writeError) { to.Write(bytes, 0, read); } } catch (Exception ex) { writeError = true; tracer.TraceError(ex); } } idleManager.UpdateActivity(); if (closeAfterCopy) { to.Close(); } } catch (Exception ex) { tracer.TraceError(ex); } }; IAsyncResult errorReader = reader.BeginInvoke(process.StandardError, null, null); IAsyncResult inputResult = null; if (input != null) { // Copy into the input stream, and close it to tell the exe it can process it inputResult = copyStream.BeginInvoke(input, process.StandardInput.BaseStream, true, null, null); } // Copy the exe's output into the output stream IAsyncResult outputResult = copyStream.BeginInvoke(process.StandardOutput.BaseStream, output, false, null, null); idleManager.WaitForExit(process); // Wait for the input operation to complete if (inputResult != null) { inputResult.AsyncWaitHandle.WaitOne(); } // Wait for the output operation to be complete outputResult.AsyncWaitHandle.WaitOne(); string error = reader.EndInvoke(errorReader); tracer.TraceProcessExitCode(process); if (process.ExitCode != 0) { throw new CommandLineException(Path, process.StartInfo.Arguments, error) { ExitCode = process.ExitCode, Error = error }; } } }
public async Task ProcesOutputBlockedTests() { var tracer = Mock.Of<ITracer>(); var process = new Mock<IProcess>(); var idleManager = new IdleManager(TimeSpan.MaxValue, tracer); var output = new Mock<Stream>(MockBehavior.Strict); CancellationToken cancellationToken; // Setup output.SetupGet(o => o.CanRead) .Returns(true); output.Setup(o => o.ReadAsync(It.IsAny<byte[]>(), 0, It.IsAny<int>(), It.IsAny<CancellationToken>())) .Callback((byte[] buffer, int offset, int count, CancellationToken token) => { cancellationToken = token; }) .Returns(async () => { await Task.Delay(5000, cancellationToken); return 0; }); process.SetupGet(p => p.StandardInput) .Returns(new StreamWriter(new MemoryStream())); process.SetupGet(p => p.StandardOutput) .Returns(new StreamReader(output.Object)); process.SetupGet(p => p.StandardError) .Returns(new StreamReader(new MemoryStream())); process.Setup(p => p.WaitForExit(It.IsAny<TimeSpan>())) .Returns(true); var timeout = ProcessExtensions.StandardOutputDrainTimeout; try { // Speedup the test ProcessExtensions.StandardOutputDrainTimeout = TimeSpan.FromSeconds(1); // Test await process.Object.Start(tracer, new MemoryStream(), new MemoryStream(), null, idleManager); throw new InvalidOperationException("Should not reach here!"); } catch (TimeoutException) { // No-op } finally { ProcessExtensions.StandardOutputDrainTimeout = timeout; } }
private static async Task FlushAllAsync(IdleManager idleManager, CancellationTokenSource cancellationTokenSource, IEnumerable<Task> tasks) { var prevActivity = DateTime.MinValue; while (true) { // Wait for either delay or io tasks var delay = Task.Delay(StandardOutputDrainTimeout, cancellationTokenSource.Token); var stdio = Task.WhenAll(tasks); var completed = await Task.WhenAny(stdio, delay); // if delay commpleted first (meaning timeout), check if activity and continue to wait if (completed == delay) { var lastActivity = idleManager.LastActivity; if (lastActivity != prevActivity) { prevActivity = lastActivity; continue; } } // clean up all pending tasks by cancelling them // this is important so we don't have runaway tasks cancellationTokenSource.Cancel(); try { // get result of stdio flush await stdio; break; } catch (TaskCanceledException) { // expected since we cancelled all tasks } // this means no activity within given time if (completed != stdio) { throw new TimeoutException("Timeout draining standard input, output and error!"); } // happy path break; } }