diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2015-03-14 00:21:35 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2015-08-11 16:37:58 +0000 |
commit | 0ec809e027c643e42103b12340855c438b9a775b (patch) | |
tree | 08bedb118ff8a34e862b39cd7d1074cc3aa1b747 /src/3rdparty | |
parent | b907dcaefe88a874488e2ad5e1c99c58873add2d (diff) |
forkfd: Add support for FreeBSD's pdfork(2) system call
pdfork(2) has semantics very close to what we want in forkfd, but not
quite. Differences:
- we still get SIGCHLD and need to do a wait4
- no support for atomic FD_CLOEXEC and O_NONBLOCK
On the SIGCHLD case: this commit is an improvement over the generic Unix
case, since we no longer need to install a SIGCHLD handler and do not
need to keep the arrays for matching PIDs and file descriptors. That
matching is done entirely inside the kernel.
However, since SIGCHLD is still sent to the process, an uncooperative
SIGCHLD handler can still "steal" our response. At least Glib is
documented not to reap children it wasn't explicitly asked to watch for
(source code matches), but other libraries are known to do waitpid(-1)
(e.g., EFL's Ecore). At least now the behavior is consistent: we will
never install a handler, so the behavior won't depend on the order in
which the handlers are installed.
Change-Id: Iee8cbc07c4434ce9b560ffff13cb4c63306e43ef
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Reviewed-by: Rafael Roquetto <rafael.roquetto@kdab.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/3rdparty')
-rw-r--r-- | src/3rdparty/forkfd/forkfd.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index e942db2717..51223aef1f 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -54,6 +54,9 @@ # define HAVE_PIPE2 1 # endif #endif +#if defined(__FreeBSD__) && __FreeBSD__ >= 9 +# include <sys/procdesc.h> +#endif #if _POSIX_VERSION-0 >= 200809L || _XOPEN_VERSION-0 >= 500 # define HAVE_WAITID 1 @@ -506,6 +509,46 @@ static int create_pipe(int filedes[], int flags) return ret; } +#if defined(FORKFD_NO_SPAWNFD) && defined(__FreeBSD__) && __FreeBSD__ >= 9 +# if __FreeBSD__ == 9 +/* PROCDESC is an optional feature in the kernel and wasn't enabled + * by default on FreeBSD 9. So we need to check for it at runtime. */ +static ffd_atomic_int system_has_forkfd = FFD_ATOMIC_INIT(1); +# else +/* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option + * anymore and can't be disabled. */ +static const int system_has_forkfd = 1; +# endif + +static int system_forkfd(int flags, pid_t *ppid) +{ + int ret; + pid_t pid; + pid = pdfork(&ret, PD_DAEMON); + if (__builtin_expect(pid == -1, 0)) { +# if __FreeBSD__ == 9 + if (errno == ENOSYS) { + /* PROCDESC wasn't compiled into the kernel: don't try it again. */ + ffd_atomic_store(&system_has_forkfd, 0, FFD_ATOMIC_RELAXED); + } +# endif + return -1; + } + if (pid == 0) { + /* child process */ + return FFD_CHILD_PROCESS; + } + + /* parent process */ + if (flags & FFD_CLOEXEC) + fcntl(ret, F_SETFD, FD_CLOEXEC); + if (flags & FFD_NONBLOCK) + fcntl(ret, F_SETFL, fcntl(ret, F_GETFL) | O_NONBLOCK); + if (ppid) + *ppid = pid; + return ret; +} +#else static const int system_has_forkfd = 0; static int system_forkfd(int flags, pid_t *ppid) { @@ -513,6 +556,7 @@ static int system_forkfd(int flags, pid_t *ppid) (void)ppid; return -1; } +#endif #ifndef FORKFD_NO_FORKFD /** @@ -748,6 +792,26 @@ int forkfd_wait(int ffd, forkfd_info *info, struct rusage *rusage) struct pipe_payload payload; int ret; + if (system_has_forkfd) { +#if defined(__FreeBSD__) && __FreeBSD__ >= 9 + pid_t pid; + int status; + int options = WEXITED; + + ret = pdgetpid(ffd, &pid); + if (ret == -1) + return ret; + ret = fcntl(ffd, F_GETFL); + if (ret == -1) + return ret; + options |= (ret & O_NONBLOCK) ? WNOHANG : 0; + ret = wait4(pid, &status, options, rusage); + if (ret != -1 && info) + convertStatusToForkfdInfo(status, info); + return ret == -1 ? -1 : 0; +#endif + } + ret = read(ffd, &payload, sizeof(payload)); if (ret == -1) return ret; /* pass errno, probably EINTR, EBADF or EWOULDBLOCK */ |