summaryrefslogtreecommitdiffstats
path: root/src/3rdparty
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2015-03-14 00:21:35 -0700
committerThiago Macieira <thiago.macieira@intel.com>2015-08-11 16:37:58 +0000
commit0ec809e027c643e42103b12340855c438b9a775b (patch)
tree08bedb118ff8a34e862b39cd7d1074cc3aa1b747 /src/3rdparty
parentb907dcaefe88a874488e2ad5e1c99c58873add2d (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.c64
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 */