diff --git a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs index 4d0854ba03..0f926deda3 100644 --- a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Detectors; using BenchmarkDotNet.Engines; @@ -13,6 +8,13 @@ using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.CoreRun; using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading.Tasks; namespace BenchmarkDotNet.Extensions @@ -210,27 +212,51 @@ private static void GetAllChildIdsUnix(int parentId, HashSet children, Time private static (int exitCode, string output) RunProcessAndReadOutput(string fileName, string arguments, TimeSpan timeout) { - var startInfo = new ProcessStartInfo + using var process = new Process { - FileName = fileName, - Arguments = arguments, - RedirectStandardOutput = true, - UseShellExecute = false + StartInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = false, + UseShellExecute = false + }, + EnableRaisingEvents = true }; - using (var process = Process.Start(startInfo)) + var stdout = new StringBuilder(); + + var tcsExited = new TaskCompletionSource(); + var tcsStdout = new TaskCompletionSource(); + + process.Exited += (_, __) => tcsExited.TrySetResult(true); + process.OutputDataReceived += (_, e) => { - if (process.WaitForExit((int)timeout.TotalMilliseconds)) - { - return (process.ExitCode, process.StandardOutput.ReadToEnd()); - } + if (e.Data != null) + stdout.AppendLine(e.Data); else - { - process.Kill(); - } + tcsStdout.TrySetResult(true); + }; - return (process.ExitCode, default); + process.Start(); + process.BeginOutputReadLine(); + + var tasks = Task.WhenAll(tcsExited.Task, tcsStdout.Task); + if (tasks.Wait(timeout)) + return (process.ExitCode, stdout.ToString()); + + // Handle timeout + try + { + process.KillTree(); } + catch + { + // Ignore exception + } + + return (process.HasExited ? process.ExitCode : -1, default); } private static int RunProcessAndIgnoreOutput(string fileName, string arguments, TimeSpan timeout) diff --git a/src/BenchmarkDotNet/Helpers/ProcessHelper.cs b/src/BenchmarkDotNet/Helpers/ProcessHelper.cs index caa3410b21..d01e2a3ddf 100644 --- a/src/BenchmarkDotNet/Helpers/ProcessHelper.cs +++ b/src/BenchmarkDotNet/Helpers/ProcessHelper.cs @@ -23,7 +23,6 @@ internal static class ProcessHelper UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, - RedirectStandardError = true }; if (environmentVariables != null) foreach (var variable in environmentVariables) diff --git a/src/BenchmarkDotNet/Validators/DotNetSdkValidator.cs b/src/BenchmarkDotNet/Validators/DotNetSdkValidator.cs index 66b6a35aca..dc83d3162f 100644 --- a/src/BenchmarkDotNet/Validators/DotNetSdkValidator.cs +++ b/src/BenchmarkDotNet/Validators/DotNetSdkValidator.cs @@ -84,11 +84,11 @@ private static IEnumerable GetInstalledDotNetSdks(string? customDotNetC return Enumerable.Empty(); } + var output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); if (process.ExitCode == 0) { - var output = process.StandardOutput.ReadToEnd(); var lines = output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); var versions = new List(lines.Count());