Exemple #1
0
        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);
            }
        }
Exemple #4
0
 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);
 }