public static IDisposable RegisterWaitForSingleObject(WaitHandle handle, Action<object, bool> callback, object state, TimeSpan timeout) { var ret = new CancelWaitHandle(); Task.Run(() => { var result = WaitHandle.WaitAny(new[] { handle, ret.WaitHandle }, timeout); ret.SetCallbackThreadId(Environment.CurrentManagedThreadId); if (result == WaitHandle.WaitTimeout) callback(state, true); else if (result == 0) callback(state, false); ret.CallbackCompleted(); }); return ret; }
public static IDisposable RegisterWaitForSingleObject(WaitHandle handle, Action <object, bool> callback, object state, TimeSpan timeout) { var ret = new CancelWaitHandle(); Task.Run(() => { var result = WaitHandle.WaitAny(new[] { handle, ret.WaitHandle }, timeout); ret.SetCallbackThreadId(Environment.CurrentManagedThreadId); if (result == WaitHandle.WaitTimeout) { callback(state, true); } else if (result == 0) { callback(state, false); } ret.CallbackCompleted(); }); return(ret); }
public void Run() { if (_alreadyStarted) { throw new InvalidOperationException("Can't run again."); } var sw = Stopwatch.StartNew(); _alreadyStarted = true; Process.Start(); if (!ShouldIgnoreConsoleOutput) { Process.BeginOutputReadLine(); Process.BeginErrorReadLine(); } while (!Process.WaitForExit(1000)) { if (MaxWorkingSetMb > 0) { long peakWorkingSet = 0; try { if (!Process.HasExited) { Process.Refresh(); peakWorkingSet = Process.PeakWorkingSet64; } } catch (InvalidOperationException) { } if (peakWorkingSet > (MaxWorkingSetMb * 1024 * 1024)) { _log.LogInformation("Attempting to kill process since it exceeded it's maximum working set size"); try { if (!Process.HasExited) { Process.Kill(); // Wait for up to a minute so the process can release it's resources Process.WaitForExit(60000); } } catch (Win32Exception) { } catch (InvalidOperationException) { } throw new InvalidOperationException( $"Process {Process.ProcessName} exceeded working set limit. Process WS:{Process.WorkingSet64}. Limit:{Process.MaxWorkingSet}"); } } if (MaxAllowedRuntimeInMinutes > 0) { if (sw.Elapsed.TotalMinutes > MaxAllowedRuntimeInMinutes) { _log.LogInformation("Attempting to kill process since it exceeded it's max allowed run time ({0} minutes)", MaxAllowedRuntimeInMinutes); try { if (!Process.HasExited) { Process.Kill(); // Wait for up to a minute so the process can release it's resources Process.WaitForExit(60000); } } catch (Win32Exception) { } catch (InvalidOperationException) { } throw new InvalidOperationException($"Process exceeded max allowed run time ({MaxAllowedRuntimeInMinutes} minutes)"); } } if (MaxVirtualMemorySizeMb > 0) { long peakVirtualMemorySize = 0; try { if (!Process.HasExited) { Process.Refresh(); peakVirtualMemorySize = Process.PeakVirtualMemorySize64; } } catch (InvalidOperationException) { } if (peakVirtualMemorySize > (MaxVirtualMemorySizeMb * 1024 * 1024)) { _log.LogInformation("Attempting to kill process since it exceeded the configured Virtual memory limit"); try { if (!Process.HasExited) { Process.Kill(); // Wait for up to a minute so the process can release it's resources Process.WaitForExit(60000); } } catch (Win32Exception) { } catch (InvalidOperationException) { } throw new InvalidOperationException( $"Process {FileName} exceeded Virtual memory limit. Process VM Size:{peakVirtualMemorySize / (1024 * 1024)} MB. Limit:{MaxVirtualMemorySizeMb} MB"); } } if (CancelWaitHandle != null && CancelWaitHandle.WaitOne(0)) { _log.LogInformation("Attempting to kill process since cancelWaitHandle is set"); try { if (!Process.HasExited) { Process.Kill(); } } catch (Win32Exception) { } catch (InvalidOperationException) { } throw new OperationCanceledException(); } } // Note that earlier, we called Process.WaitForExit(1000). // https://msdn.microsoft.com/en-us/library/fb4aw7b8(v=vs.110).aspx // According to msdn documentation, that overload has a caveat: // When standard output has been redirected to asynchronous event handlers, // it is possible that output processing will not have completed when this method returns. // To ensure that asynchronous event handling has been completed, // call the WaitForExit() overload that takes no parameter after receiving a true // from this overload. To help ensure that the Exited event is handled correctly // in Windows Forms applications, set the SynchronizingObject property. // Hence, we call the overload with no parameter here. Process.WaitForExit(); lock (_syncObject) { StandardOutput = _sbStdOut.ToString(); StandardError = _sbStdErr.ToString(); } ExitCode = Process.ExitCode; }