public int Execute()

in src/common/IO/Platform.cs [75:215]


        public int Execute(
            string executable,
            string command,
            Action<string> stdOutCallback,
            Action<string> stdErrCallback,
            IDictionary<string, string> envVariables,
            TimeSpan timeout,
            CancellationToken cancellationToken,
            out string stdOutOutput,
            out string stdErrOutput)
        {
            cancellationToken.ThrowIfCancellationRequested();

            ProcessStartInfo psi = new ProcessStartInfo()
            {
                FileName = executable,
                Arguments = command,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true
            };

            var process = new ProcessEx(psi);

            if (envVariables != null)
            {
                foreach (KeyValuePair<string, string> env in envVariables)
                {
                    process.StartInfo.EnvironmentVariables[env.Key] = env.Value;
                }
            }

            using (var outputWaitCountdown = new CountdownEvent(2)) // 2 for stdout and stderr
            {
                StringBuilder stdOutLines = new StringBuilder();
                StringBuilder stdErrLines = new StringBuilder();
                object stdOutLock = new object();
                object stdErrLock = new object();
                void outputHandler(object sender, DataReceivedEventArgs e)
                {
                    if (e.Data != null)
                    {
                        lock (stdOutLock)
                        {
                            stdOutLines.AppendLine(e.Data);
                        }

                        stdOutCallback?.Invoke(e.Data);
                    }
                    else
                    {
                        try { process.OutputDataReceived -= outputHandler; } catch { }
                        try
                        {
                            // Output data has finished
                            outputWaitCountdown.Signal();
                        }
                        catch (ObjectDisposedException)
                        { }
                    }
                };
                void errorHandler(object sender, DataReceivedEventArgs e)
                {
                    if (e.Data != null)
                    {
                        lock (stdErrLock)
                        {
                            stdErrLines.AppendLine(e.Data);
                        }

                        stdErrCallback?.Invoke(e.Data);
                    }
                    else
                    {
                        try { process.ErrorDataReceived -= errorHandler; } catch { }
                        try
                        {
                            // Error data has finished
                            outputWaitCountdown.Signal();
                        }
                        catch (ObjectDisposedException)
                        { }
                    }
                }
                process.OutputDataReceived += outputHandler;
                process.ErrorDataReceived += errorHandler;

                Action killProcess =
                    () =>
                    {
                        try
                        {
                            if (!process.HasExited)
                            {
                                process.Kill();
                                process.Dispose();
                            }
                        }
                        catch (Exception e)
                        {
                            stdErrLines.AppendLine(e.ToString());
                        }
                    };

                int exitCode;
                using (cancellationToken.Register(killProcess))
                {
                    process.Start();
                    process.BeginErrorReadLine();
                    process.BeginOutputReadLine();

                    int timeoutMs = timeout.TotalMilliseconds > int.MaxValue ? int.MaxValue : (int)timeout.TotalMilliseconds;
                    exitCode = WaitForProcessExit(process, timeoutMs, stdOutCallback, stdErrCallback);
                }

                try
                {
                    // Wait for all output to flush
                    // The process should already be exited at this point, so don't wait too long before giving up
                    if (!outputWaitCountdown.Wait(TimeSpan.FromSeconds(5)))
                    {
                        throw new IOException("Timed out waiting for process output to flush");
                    }
                }
                finally
                {
                    lock (stdOutLock)
                    {
                        stdOutOutput = stdOutLines.ToString();
                    }

                    lock (stdErrLock)
                    {
                        stdErrOutput = stdErrLines.ToString();
                    }

                    process.Dispose();
                }
                return exitCode;
            }
        }