Bug 1814551 - Try waiting for child processes directly when the fork server is in use. r=nika

When the fork server is enabled, not all IPC child processes are children of
the fork server; currently, process types other than content processes
are still spawned directly.  This means that we need to `waitid` or
`waitpid` them when they exit in order to not leak zombie processes.

Specifically, we can just try to `waitid` the process, and then if that
fails with `ECHILD` we can assume it was a fork server child and fall
back to the previous `kill(pid, 0)` workaround.  This patch does that,
but only if the fork server is active; otherwise we maintain the current
behavior of only waiting for child processes directly.

Differential Revision: https://phabricator.services.mozilla.com/D168756
This commit is contained in:
Jed Davis 2023-02-09 01:48:13 +00:00
Родитель f0b145fdd2
Коммит e38f0ca71d
1 изменённых файлов: 26 добавлений и 12 удалений

Просмотреть файл

@ -30,6 +30,7 @@
#include "base/dir_reader_posix.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
// For PR_DuplicateEnvironment:
#include "prenv.h"
#include "prmem.h"
@ -196,20 +197,25 @@ void CloseSuperfluousFds(void* aCtx, bool (*aShouldPreserve)(void*, int)) {
}
bool IsProcessDead(ProcessHandle handle, bool blocking) {
auto handleForkServer = [handle]() -> mozilla::Maybe<bool> {
#ifdef MOZ_ENABLE_FORKSERVER
if (mozilla::ipc::ForkServiceChild::Get()) {
// We only know if a process exists, but not if it has crashed.
//
// Since content processes are not direct children of the chrome
// process any more, it is impossible to use |waitpid()| to wait for
// them.
const int r = kill(handle, 0);
// FIXME: for unexpected errors we should probably log a warning
// and return true, so that the caller doesn't loop / hang /
// try to kill the process. (Bug 1658072 will rewrite this code.)
return r < 0 && errno == ESRCH;
}
if (errno == ECHILD && mozilla::ipc::ForkServiceChild::Get()) {
// We only know if a process exists, but not if it has crashed.
//
// Since content processes are not direct children of the chrome
// process any more, it is impossible to use |waitpid()| to wait for
// them.
const int r = kill(handle, 0);
// FIXME: for unexpected errors we should probably log a warning
// and return true, so that the caller doesn't loop / hang /
// try to kill the process. (Bug 1658072 will rewrite this code.)
return mozilla::Some(r < 0 && errno == ESRCH);
}
#else
mozilla::Unused << handle;
#endif
return mozilla::Nothing();
};
#ifdef HAVE_WAITID
@ -222,6 +228,10 @@ bool IsProcessDead(ProcessHandle handle, bool blocking) {
const int wflags = WEXITED | WNOWAIT | (blocking ? 0 : WNOHANG);
int result = HANDLE_EINTR(waitid(P_PID, handle, &si, wflags));
if (result == -1) {
if (auto forkServerReturn = handleForkServer()) {
return *forkServerReturn;
}
// This shouldn't happen, but sometimes it does. The error is
// probably ECHILD and the reason is probably that a pid was
// waited on again after a previous wait reclaimed its zombie.
@ -287,6 +297,10 @@ bool IsProcessDead(ProcessHandle handle, bool blocking) {
int status;
const int result = waitpid(handle, &status, blocking ? 0 : WNOHANG);
if (result == -1) {
if (auto forkServerReturn = handleForkServer()) {
return *forkServerReturn;
}
CHROMIUM_LOG(ERROR) << "waitpid failed pid:" << handle
<< " errno:" << errno;
return true;