TeamCity.CSharpInteractive/ProcessRunner.cs (86 lines of code) (raw):

// ReSharper disable ClassNeverInstantiated.Global // ReSharper disable ForCanBeConvertedToForeach namespace TeamCity.CSharpInteractive; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using HostApi; internal class ProcessRunner : IProcessRunner { private readonly Func<IProcessManager> _processManagerFactory; public ProcessRunner(Func<IProcessManager> processManagerFactory) => _processManagerFactory = processManagerFactory; public ProcessResult Run(ProcessInfo processInfo, TimeSpan timeout) { var processManager = _processManagerFactory(); var process = new Process(processInfo, processManager); if (!process.TryStart(out var processResult)) { return processResult; } var finished = processManager.WaitForExit(timeout); return process.Finish(finished); } public async Task<ProcessResult> RunAsync(ProcessInfo processInfo, CancellationToken cancellationToken) { var processManager = _processManagerFactory(); var process = new Process(processInfo, processManager); if (!process.TryStart(out var processResult)) { return processResult; } await processManager.WaitForExitAsync(cancellationToken); var finished = !cancellationToken.IsCancellationRequested; return process.Finish(finished); } private class Process { public Process(ProcessInfo processInfo, IProcessManager processManager) { ProcessInfo = processInfo; ProcessManager = processManager; Stopwatch = new Stopwatch(); } private IProcessManager ProcessManager { get; } private ProcessInfo ProcessInfo { get; } private Stopwatch Stopwatch { get; } private IStartInfo StartInfo => ProcessInfo.StartInfo; private IProcessMonitor Monitor => ProcessInfo.Monitor; private Action<Output>? Handler => ProcessInfo.Handler; public bool TryStart([MaybeNullWhen(true)] out ProcessResult processResult) { if (Handler != default) { ProcessManager.OnOutput += Handler; } Stopwatch.Start(); if (!ProcessManager.Start(StartInfo, out var error)) { Stopwatch.Stop(); { processResult = Monitor.Finished(StartInfo, Stopwatch.ElapsedMilliseconds, ProcessState.Failed, default, error); if (Handler != default) { ProcessManager.OnOutput -= Handler; } return false; } } Monitor.Started(StartInfo, ProcessManager.Id); processResult = default; return true; } public ProcessResult Finish(bool finished) { if (Handler != default) { ProcessManager.OnOutput -= Handler; } if (finished) { Stopwatch.Stop(); return Monitor.Finished(StartInfo, Stopwatch.ElapsedMilliseconds, ProcessState.Finished, ProcessManager.ExitCode); } ProcessManager.Kill(); Stopwatch.Stop(); return Monitor.Finished(StartInfo, Stopwatch.ElapsedMilliseconds, ProcessState.Canceled); } } }