Fix forkfd on OS X 10.7 and earlier by avoiding waitid altogether

On OS X 10.7 and earlier, waitid() never sets si_pid, even when using
P_PID. So on OS X, check if waitid() works, and if not, use the same
codepath as if HAVE_WAITID were not defined.

Change-Id: I64331a090f9358bb01f435954d3dfd3ab430a96c
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Dyami Caliri 2015-05-24 09:04:24 -07:00
parent 9749ddeb4b
commit 970241a11a

View File

@ -63,7 +63,7 @@
# include <Availability.h> # include <Availability.h>
# include <AvailabilityMacros.h> # include <AvailabilityMacros.h>
# if MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 # if MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
# define HAVE_BROKEN_WAITID_ALL 1 # define HAVE_BROKEN_WAITID 1
# endif # endif
#endif #endif
@ -109,10 +109,10 @@ static struct sigaction old_sigaction;
static pthread_once_t forkfd_initialization = PTHREAD_ONCE_INIT; static pthread_once_t forkfd_initialization = PTHREAD_ONCE_INIT;
static ffd_atomic_int forkfd_status = FFD_ATOMIC_INIT(0); static ffd_atomic_int forkfd_status = FFD_ATOMIC_INIT(0);
#ifdef HAVE_BROKEN_WAITID_ALL #ifdef HAVE_BROKEN_WAITID
static int waitid_p_all_works = 0; static int waitid_works = 0;
#else #else
static const int waitid_p_all_works = 1; static const int waitid_works = 1;
#endif #endif
static ProcessInfo *tryAllocateInSection(Header *header, ProcessInfo entries[], int maxCount) static ProcessInfo *tryAllocateInSection(Header *header, ProcessInfo entries[], int maxCount)
@ -183,10 +183,13 @@ static int tryReaping(pid_t pid, siginfo_t *info)
{ {
/* reap the child */ /* reap the child */
#ifdef HAVE_WAITID #ifdef HAVE_WAITID
// we have waitid(2), which fills in siginfo_t for us if (waitid_works) {
info->si_pid = 0; // we have waitid(2), which fills in siginfo_t for us
return waitid(P_PID, pid, info, WEXITED | WNOHANG) == 0 && info->si_pid == pid; info->si_pid = 0;
#else return waitid(P_PID, pid, info, WEXITED | WNOHANG) == 0 && info->si_pid == pid;
}
#endif
int status; int status;
if (waitpid(pid, &status, WNOHANG) <= 0) if (waitpid(pid, &status, WNOHANG) <= 0)
return 0; // child did not change state return 0; // child did not change state
@ -206,7 +209,6 @@ static int tryReaping(pid_t pid, siginfo_t *info)
} }
return 1; return 1;
#endif
} }
static void freeInfo(Header *header, ProcessInfo *entry) static void freeInfo(Header *header, ProcessInfo *entry)
@ -246,7 +248,7 @@ static void sigchld_handler(int signum)
memset(&info, 0, sizeof info); memset(&info, 0, sizeof info);
#ifdef HAVE_WAITID #ifdef HAVE_WAITID
if (!waitid_p_all_works) if (!waitid_works)
goto search_arrays; goto search_arrays;
/* be optimistic: try to see if we can get the child that exited */ /* be optimistic: try to see if we can get the child that exited */
@ -310,12 +312,14 @@ search_arrays:
if (pid <= 0) if (pid <= 0)
continue; continue;
#ifdef HAVE_WAITID #ifdef HAVE_WAITID
/* The child might have been reaped by the block above in another thread, if (waitid_works) {
* so first check if it's ready and, if it is, lock it */ /* The child might have been reaped by the block above in another thread,
if (!isChildReady(pid, &info) || * so first check if it's ready and, if it is, lock it */
!ffd_atomic_compare_exchange(&children.entries[i].pid, &pid, -1, if (!isChildReady(pid, &info) ||
FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED)) !ffd_atomic_compare_exchange(&children.entries[i].pid, &pid, -1,
continue; FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED))
continue;
}
#endif #endif
if (tryReaping(pid, &info)) { if (tryReaping(pid, &info)) {
/* this is our child, send notification and free up this entry */ /* this is our child, send notification and free up this entry */
@ -331,12 +335,14 @@ search_arrays:
if (pid <= 0) if (pid <= 0)
continue; continue;
#ifdef HAVE_WAITID #ifdef HAVE_WAITID
/* The child might have been reaped by the block above in another thread, if (waitid_works) {
* so first check if it's ready and, if it is, lock it */ /* The child might have been reaped by the block above in another thread,
if (!isChildReady(pid, &info) || * so first check if it's ready and, if it is, lock it */
!ffd_atomic_compare_exchange(&array->entries[i].pid, &pid, -1, if (!isChildReady(pid, &info) ||
FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED)) !ffd_atomic_compare_exchange(&array->entries[i].pid, &pid, -1,
continue; FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED))
continue;
}
#endif #endif
if (tryReaping(pid, &info)) { if (tryReaping(pid, &info)) {
/* this is our child, send notification and free up this entry */ /* this is our child, send notification and free up this entry */
@ -357,17 +363,19 @@ chain_handler:
static void forkfd_initialize() static void forkfd_initialize()
{ {
#if defined(HAVE_BROKEN_WAITID_ALL) #if defined(HAVE_BROKEN_WAITID)
pid_t pid = fork(); pid_t pid = fork();
if (pid == 0) { if (pid == 0) {
_exit(0); _exit(0);
} else if (pid > 0) { } else if (pid > 0) {
siginfo_t info; siginfo_t info;
waitid(P_ALL, 0, &info, WNOWAIT | WEXITED); waitid(P_ALL, 0, &info, WNOWAIT | WEXITED);
waitid_p_all_works = (info.si_pid != 0); waitid_works = (info.si_pid != 0);
info.si_pid = 0;
// now really reap the child // now really reap the child
waitid(P_PID, pid, &info, WEXITED); waitid(P_PID, pid, &info, WEXITED);
waitid_works = waitid_works && (info.si_pid != 0);
} }
#endif #endif