Skip to content

Commit a8e690e

Browse files
Process.Unix: while reaping all processes, handle encountering direct children. (#80433)
The process that runs as pid 1 is responsible for reaping orphaned processes. Since .NET 7, .NET applications running as pid 1 assume this responsibility. The code meant for reaping orphaned processes didn't account for encountering direct children. These child processes get reaped without updating the internal state. When the code later tries to reap such a child process it causes a FailFast because the process is missing. Co-authored-by: Tom Deseyn <tom.deseyn@gmail.com>
1 parent b84e2f6 commit a8e690e

File tree

1 file changed

+34
-13
lines changed

1 file changed

+34
-13
lines changed

src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessWaitState.Unix.cs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,24 @@ private Task WaitForExitAsync(CancellationToken cancellationToken = default)
551551
}, cancellationToken);
552552
}
553553

554+
private void ChildReaped(int exitCode, bool configureConsole)
555+
{
556+
lock (_gate)
557+
{
558+
Debug.Assert(!_exited);
559+
560+
_exitCode = exitCode;
561+
562+
if (_usesTerminal)
563+
{
564+
// Update terminal settings before calling SetExited.
565+
Process.ConfigureTerminalForChildProcesses(-1, configureConsole);
566+
}
567+
568+
SetExited();
569+
}
570+
}
571+
554572
private bool TryReapChild(bool configureConsole)
555573
{
556574
lock (_gate)
@@ -566,16 +584,7 @@ private bool TryReapChild(bool configureConsole)
566584

567585
if (waitResult == _processId)
568586
{
569-
_exitCode = exitCode;
570-
571-
if (_usesTerminal)
572-
{
573-
// Update terminal settings before calling SetExited.
574-
Process.ConfigureTerminalForChildProcesses(-1, configureConsole);
575-
}
576-
577-
SetExited();
578-
587+
ChildReaped(exitCode, configureConsole);
579588
return true;
580589
}
581590
else if (waitResult == 0)
@@ -636,7 +645,7 @@ internal static void CheckChildren(bool reapAll, bool configureConsole)
636645
}
637646
} while (pid > 0);
638647

639-
if (checkAll)
648+
if (checkAll && !reapAll)
640649
{
641650
// We track things to unref so we don't invalidate our iterator by changing s_childProcessWaitStates.
642651
ProcessWaitState? firstToRemove = null;
@@ -675,8 +684,20 @@ internal static void CheckChildren(bool reapAll, bool configureConsole)
675684
{
676685
do
677686
{
678-
pid = Interop.Sys.WaitPidExitedNoHang(-1, out _);
679-
} while (pid > 0);
687+
int exitCode;
688+
pid = Interop.Sys.WaitPidExitedNoHang(-1, out exitCode);
689+
if (pid <= 0)
690+
{
691+
break;
692+
}
693+
694+
// Check if the process is a child that has just terminated.
695+
if (s_childProcessWaitStates.TryGetValue(pid, out ProcessWaitState? pws))
696+
{
697+
pws.ChildReaped(exitCode, configureConsole);
698+
pws.ReleaseRef();
699+
}
700+
} while (true);
680701
}
681702
}
682703
}

0 commit comments

Comments
 (0)