internal ProcessWaitHandle(ProcessWaitState processWaitState) { // Get the wait state's event, and use that event's safe wait handle // in place of ours. This will let code register for completion notifications // on this ProcessWaitHandle and be notified when the wait state's handle completes. ManualResetEvent mre = processWaitState.EnsureExitedEvent(); this.SetSafeWaitHandle(mre.GetSafeWaitHandle()); }
public void Dispose() { if (_state != null) { GC.SuppressFinalize(this); _state.ReleaseRef(); _state = null; } }
/// <summary> /// Ensures that the mapping table contains an entry for the process ID, /// increments its ref count, and returns it. /// </summary> /// <param name="processId">The process ID for which we need wait state.</param> /// <returns>The wait state object.</returns> internal static ProcessWaitState AddRef(int processId, bool isNewChild, bool usesTerminal) { lock (s_childProcessWaitStates) { ProcessWaitState pws; if (isNewChild) { // When the PID is recycled for a new child, we remove the old child. s_childProcessWaitStates.Remove(processId); pws = new ProcessWaitState(processId, isChild: true, usesTerminal); s_childProcessWaitStates.Add(processId, pws); pws._outstandingRefCount++; // For Holder pws._outstandingRefCount++; // Decremented in CheckChildren } else { lock (s_processWaitStates) { DateTime exitTime = default; // We are referencing an existing process. // This may be a child process, so we check s_childProcessWaitStates too. if (s_childProcessWaitStates.TryGetValue(processId, out pws)) { // child process } else if (s_processWaitStates.TryGetValue(processId, out pws)) { // This is best effort for dealing with recycled pids for non-child processes. // As long as we haven't observed process exit, it's safe to share the ProcessWaitState. // Once we've observed the exit, we'll create a new ProcessWaitState just in case // this may be a recycled pid. // If it wasn't, that ProcessWaitState will observe too that the process has exited. // We pass the ExitTime so it can be the same, but we'll clear it when we see there // is a live process with that pid. if (pws.GetExited(out _, refresh: false)) { s_processWaitStates.Remove(processId); exitTime = pws.ExitTime; pws = null; } } if (pws == null) { pws = new ProcessWaitState(processId, isChild: false, usesTerminal: false, exitTime); s_processWaitStates.Add(processId, pws); } pws._outstandingRefCount++; } } return(pws); } }
private static void OnSigChild(bool reapAll) { // Lock to avoid races with Process.Start s_processStartLock.EnterWriteLock(); try { ProcessWaitState.CheckChildren(reapAll); } finally { s_processStartLock.ExitWriteLock(); } }
internal static void CheckChildren(bool reapAll) { // This is called on SIGCHLD from a native thread. // A lock in Process ensures no new processes are spawned while we are checking. lock (s_childProcessWaitStates) { bool checkAll = false; // Check terminated processes. int pid; do { // Find a process that terminated without reaping it yet. pid = Interop.Sys.WaitIdAnyExitedNoHangNoWait(); if (pid > 0) { if (s_childProcessWaitStates.TryGetValue(pid, out ProcessWaitState pws)) { // Known Process. if (pws.TryReapChild()) { pws.ReleaseRef(); } } else { // unlikely: This is not a managed Process, so we are not responsible for reaping. // Fall back to checking all Processes. checkAll = true; break; } } else if (pid == 0) { // No more terminated children. } else { // Unexpected. int errorCode = Marshal.GetLastWin32Error(); Environment.FailFast("Error while checking for terminated children. errno = " + errorCode); } } while (pid > 0); if (checkAll) { // We track things to unref so we don't invalidate our iterator by changing s_childProcessWaitStates. ProcessWaitState firstToRemove = null; List <ProcessWaitState> additionalToRemove = null; foreach (KeyValuePair <int, ProcessWaitState> kv in s_childProcessWaitStates) { ProcessWaitState pws = kv.Value; if (pws.TryReapChild()) { if (firstToRemove == null) { firstToRemove = pws; } else { if (additionalToRemove == null) { additionalToRemove = new List <ProcessWaitState>(); } additionalToRemove.Add(pws); } } } if (firstToRemove != null) { firstToRemove.ReleaseRef(); if (additionalToRemove != null) { foreach (ProcessWaitState pws in additionalToRemove) { pws.ReleaseRef(); } } } } if (reapAll) { do { int exitCode; pid = Interop.Sys.WaitPidExitedNoHang(-1, out exitCode); } while (pid > 0); } } }
internal Holder(int processId, bool isNewChild = false, bool usesTerminal = false) { _state = ProcessWaitState.AddRef(processId, isNewChild, usesTerminal); }