summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/forkfd
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/forkfd')
-rw-r--r--src/3rdparty/forkfd/forkfd.c188
-rw-r--r--src/3rdparty/forkfd/forkfd.h1
-rw-r--r--src/3rdparty/forkfd/forkfd_c11.h4
-rw-r--r--src/3rdparty/forkfd/forkfd_freebsd.c2
-rw-r--r--src/3rdparty/forkfd/forkfd_linux.c65
-rw-r--r--src/3rdparty/forkfd/qt_attribution.json4
6 files changed, 189 insertions, 75 deletions
diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c
index c29ebc299d..edef3c5bcc 100644
--- a/src/3rdparty/forkfd/forkfd.c
+++ b/src/3rdparty/forkfd/forkfd.c
@@ -72,6 +72,10 @@
# undef HAVE_WAITID
#endif
+#if (defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1300000)
+# include <sys/eventfd.h>
+# define HAVE_EVENTFD 1
+#endif
#if (defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1000032) || \
(defined(__OpenBSD__) && OpenBSD >= 201505) || \
(defined(__NetBSD__) && __NetBSD_Version__ >= 600000000)
@@ -99,6 +103,7 @@
static int system_has_forkfd(void);
static int system_forkfd(int flags, pid_t *ppid, int *system);
+static int system_vforkfd(int flags, pid_t *ppid, int (*)(void *), void *, int *system);
static int system_forkfd_wait(int ffd, struct forkfd_info *info, int ffdwoptions, struct rusage *rusage);
static int disable_fork_fallback(void)
@@ -107,7 +112,7 @@ static int disable_fork_fallback(void)
/* if there's no system forkfd, we have to use the fallback */
return system_has_forkfd();
#else
- return false;
+ return 0;
#endif
}
@@ -595,46 +600,7 @@ static int create_pipe(int filedes[], int flags)
}
#ifndef FORKFD_NO_FORKFD
-/**
- * @brief forkfd returns a file descriptor representing a child process
- * @return a file descriptor, or -1 in case of failure
- *
- * forkfd() creates a file descriptor that can be used to be notified of when a
- * child process exits. This file descriptor can be monitored using select(2),
- * poll(2) or similar mechanisms.
- *
- * The @a flags parameter can contain the following values ORed to change the
- * behaviour of forkfd():
- *
- * @li @c FFD_NONBLOCK Set the O_NONBLOCK file status flag on the new open file
- * descriptor. Using this flag saves extra calls to fnctl(2) to achieve the same
- * result.
- *
- * @li @c FFD_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file
- * descriptor. You probably want to set this flag, since forkfd() does not work
- * if the original parent process dies.
- *
- * @li @c FFD_USE_FORK Tell forkfd() to actually call fork() instead of a
- * different system implementation that may be available. On systems where a
- * different implementation is available, its behavior may differ from that of
- * fork(), such as not calling the functions registered with pthread_atfork().
- * If that's necessary, pass this flag.
- *
- * The file descriptor returned by forkfd() supports the following operations:
- *
- * @li read(2) When the child process exits, then the buffer supplied to
- * read(2) is used to return information about the status of the child in the
- * form of one @c siginfo_t structure. The buffer must be at least
- * sizeof(siginfo_t) bytes. The return value of read(2) is the total number of
- * bytes read.
- *
- * @li poll(2), select(2) (and similar) The file descriptor is readable (the
- * select(2) readfds argument; the poll(2) POLLIN flag) if the child has exited
- * or signalled via SIGCHLD.
- *
- * @li close(2) When the file descriptor is no longer required it should be closed.
- */
-int forkfd(int flags, pid_t *ppid)
+static int forkfd_fork_fallback(int flags, pid_t *ppid)
{
Header *header;
ProcessInfo *info;
@@ -643,18 +609,7 @@ int forkfd(int flags, pid_t *ppid)
int death_pipe[2];
int sync_pipe[2];
int ret;
-#ifdef __linux__
- int efd;
-#endif
-
- if (disable_fork_fallback())
- flags &= ~FFD_USE_FORK;
-
- if ((flags & FFD_USE_FORK) == 0) {
- fd = system_forkfd(flags, ppid, &ret);
- if (ret || disable_fork_fallback())
- return fd;
- }
+ int efd = -1;
(void) pthread_once(&forkfd_initialization, forkfd_initialize);
@@ -671,9 +626,8 @@ int forkfd(int flags, pid_t *ppid)
#ifdef HAVE_EVENTFD
/* try using an eventfd, which consumes less resources */
efd = eventfd(0, EFD_CLOEXEC);
- if (efd == -1)
#endif
- {
+ if (efd == -1) {
/* try a pipe */
if (create_pipe(sync_pipe, FFD_CLOEXEC) == -1) {
/* failed both at eventfd and pipe; fail and pass errno */
@@ -700,14 +654,13 @@ int forkfd(int flags, pid_t *ppid)
if (pid == 0) {
/* this is the child process */
/* first, wait for the all clear */
-#ifdef HAVE_EVENTFD
if (efd != -1) {
+#ifdef HAVE_EVENTFD
eventfd_t val64;
EINTR_LOOP(ret, eventfd_read(efd, &val64));
EINTR_LOOP(ret, close(efd));
- } else
#endif
- {
+ } else {
char c;
EINTR_LOOP(ret, close(sync_pipe[1]));
EINTR_LOOP(ret, read(sync_pipe[0], &c, sizeof c));
@@ -764,6 +717,112 @@ err_free:
freeInfo(header, info);
return -1;
}
+
+/**
+ * @brief forkfd returns a file descriptor representing a child process
+ * @return a file descriptor, or -1 in case of failure
+ *
+ * forkfd() creates a file descriptor that can be used to be notified of when a
+ * child process exits. This file descriptor can be monitored using select(2),
+ * poll(2) or similar mechanisms.
+ *
+ * The @a flags parameter can contain the following values ORed to change the
+ * behaviour of forkfd():
+ *
+ * @li @c FFD_NONBLOCK Set the O_NONBLOCK file status flag on the new open file
+ * descriptor. Using this flag saves extra calls to fnctl(2) to achieve the same
+ * result.
+ *
+ * @li @c FFD_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file
+ * descriptor. You probably want to set this flag, since forkfd() does not work
+ * if the original parent process dies.
+ *
+ * @li @c FFD_USE_FORK Tell forkfd() to actually call fork() instead of a
+ * different system implementation that may be available. On systems where a
+ * different implementation is available, its behavior may differ from that of
+ * fork(), such as not calling the functions registered with pthread_atfork().
+ * If that's necessary, pass this flag.
+ *
+ * The file descriptor returned by forkfd() supports the following operations:
+ *
+ * @li read(2) When the child process exits, then the buffer supplied to
+ * read(2) is used to return information about the status of the child in the
+ * form of one @c siginfo_t structure. The buffer must be at least
+ * sizeof(siginfo_t) bytes. The return value of read(2) is the total number of
+ * bytes read.
+ *
+ * @li poll(2), select(2) (and similar) The file descriptor is readable (the
+ * select(2) readfds argument; the poll(2) POLLIN flag) if the child has exited
+ * or signalled via SIGCHLD.
+ *
+ * @li close(2) When the file descriptor is no longer required it should be closed.
+ */
+int forkfd(int flags, pid_t *ppid)
+{
+ int fd;
+ if (disable_fork_fallback())
+ flags &= ~FFD_USE_FORK;
+
+ if ((flags & FFD_USE_FORK) == 0) {
+ int system_forkfd_works;
+ fd = system_forkfd(flags, ppid, &system_forkfd_works);
+ if (system_forkfd_works || disable_fork_fallback())
+ return fd;
+ }
+
+ return forkfd_fork_fallback(flags, ppid);
+}
+
+/**
+ * @brief vforkfd returns a file descriptor representing a child process
+ * @return a file descriptor, or -1 in case of failure
+ *
+ * vforkfd() operates in the same way as forkfd() and the @a flags and @a ppid
+ * arguments are the same. See the forkfd() documentation for details on the
+ * possible values and information on the returned file descriptor.
+ *
+ * This function does not return @c FFD_CHILD_PROCESS. Instead, the function @a
+ * childFn is called in the child process with the @a token parameter as
+ * argument. If that function returns, its return value will be passed to
+ * _exit(2).
+ *
+ * This function differs from forkfd() the same way that vfork() differs from
+ * fork(): the parent process may be suspended while the child is has not yet
+ * called _exit(2) or execve(2). Additionally, on some systems, the child
+ * process may share memory with the parent process the same way an auxiliary
+ * thread would, so extreme care should be employed on what functions the child
+ * process uses before termination.
+ *
+ * The @c FFD_USE_FORK flag retains its behavior as described in the forkfd()
+ * documentation, including that of actually using fork(2) and no other
+ * implementation.
+ *
+ * Currently, only on Linux will this function have any behavior different from
+ * forkfd(). In all other systems, it is equivalent to the following code:
+ *
+ * @code
+ * int ffd = forkfd(flags, &pid);
+ * if (ffd == FFD_CHILD_PROCESS)
+ * _exit(childFn(token));
+ * @endcode
+ */
+int vforkfd(int flags, pid_t *ppid, int (*childFn)(void *), void *token)
+{
+ int fd;
+ if ((flags & FFD_USE_FORK) == 0) {
+ int system_forkfd_works;
+ fd = system_vforkfd(flags, ppid, childFn, token, &system_forkfd_works);
+ if (system_forkfd_works || disable_fork_fallback())
+ return fd;
+ }
+
+ fd = forkfd_fork_fallback(flags, ppid);
+ if (fd == FFD_CHILD_PROCESS) {
+ /* child process */
+ _exit(childFn(token));
+ }
+ return fd;
+}
#endif // FORKFD_NO_FORKFD
#if _POSIX_SPAWN > 0 && !defined(FORKFD_NO_SPAWNFD)
@@ -889,3 +948,16 @@ int system_forkfd_wait(int ffd, struct forkfd_info *info, int options, struct ru
return -1;
}
#endif
+#ifndef SYSTEM_FORKFD_CAN_VFORK
+int system_vforkfd(int flags, pid_t *ppid, int (*childFn)(void *), void *token, int *system)
+{
+ /* we don't have a way to vfork(), so fake it */
+ int ret = system_forkfd(flags, ppid, system);
+ if (ret == FFD_CHILD_PROCESS) {
+ /* child process */
+ _exit(childFn(token));
+ }
+ return ret;
+}
+#endif
+#undef SYSTEM_FORKFD_CAN_VFORK
diff --git a/src/3rdparty/forkfd/forkfd.h b/src/3rdparty/forkfd/forkfd.h
index 6bc1f0c1b9..16cde67c8a 100644
--- a/src/3rdparty/forkfd/forkfd.h
+++ b/src/3rdparty/forkfd/forkfd.h
@@ -53,6 +53,7 @@ struct forkfd_info {
};
int forkfd(int flags, pid_t *ppid);
+int vforkfd(int flags, pid_t *ppid, int (*childFn)(void *), void *token);
int forkfd_wait4(int ffd, struct forkfd_info *info, int options, struct rusage *rusage);
static inline int forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage)
{
diff --git a/src/3rdparty/forkfd/forkfd_c11.h b/src/3rdparty/forkfd/forkfd_c11.h
index 2b1d3f181e..934ce55a11 100644
--- a/src/3rdparty/forkfd/forkfd_c11.h
+++ b/src/3rdparty/forkfd/forkfd_c11.h
@@ -48,7 +48,11 @@ typedef std::atomic<int> ffd_atomic_int;
typedef atomic_int ffd_atomic_int;
#endif
+#ifdef __cpp_lib_atomic_value_initialization
+#define FFD_ATOMIC_INIT(val) { val }
+#else
#define FFD_ATOMIC_INIT(val) ATOMIC_VAR_INIT(val)
+#endif
#define ffd_atomic_load(ptr, order) \
atomic_load_explicit(ptr, order)
diff --git a/src/3rdparty/forkfd/forkfd_freebsd.c b/src/3rdparty/forkfd/forkfd_freebsd.c
index c4ca796ccd..ba18d83591 100644
--- a/src/3rdparty/forkfd/forkfd_freebsd.c
+++ b/src/3rdparty/forkfd/forkfd_freebsd.c
@@ -29,6 +29,8 @@
#include "forkfd_atomic.h"
+#undef SYSTEM_FORKFD_CAN_VFORK
+
// in forkfd.c
static int convertForkfdWaitFlagsToWaitFlags(int ffdoptions);
static void convertStatusToForkfdInfo(int status, struct forkfd_info *info);
diff --git a/src/3rdparty/forkfd/forkfd_linux.c b/src/3rdparty/forkfd/forkfd_linux.c
index c86e138b63..4dacc1919d 100644
--- a/src/3rdparty/forkfd/forkfd_linux.c
+++ b/src/3rdparty/forkfd/forkfd_linux.c
@@ -51,6 +51,8 @@
# define P_PIDFD 3
#endif
+#define SYSTEM_FORKFD_CAN_VFORK
+
// in forkfd.c
static int convertForkfdWaitFlagsToWaitFlags(int ffdoptions);
static void convertStatusToForkfdInfo(int status, struct forkfd_info *info);
@@ -82,7 +84,8 @@ static int sys_clone(unsigned long cloneflags, int *ptid)
return syscall(__NR_clone, cloneflags, child_stack, stack_size, ptid, newtls, ctid);
#elif defined(__arc__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
defined(__nds32__) || defined(__hppa__) || defined(__powerpc__) || defined(__i386__) || \
- defined(__x86_64__) || defined(__xtensa__) || defined(__alpha__) || defined(__riscv)
+ defined(__x86_64__) || defined(__xtensa__) || defined(__alpha__) || defined(__riscv) || \
+ defined(__loongarch__)
/* ctid and newtls are inverted on CONFIG_CLONE_BACKWARDS architectures,
* but since both values are 0, there's no harm. */
return syscall(__NR_clone, cloneflags, child_stack, ptid, ctid, newtls);
@@ -131,16 +134,55 @@ int system_has_forkfd()
return ffd_atomic_load(&system_forkfd_state, FFD_ATOMIC_RELAXED) > 0;
}
-int system_forkfd(int flags, pid_t *ppid, int *system)
+static int system_forkfd_availability(void)
{
- pid_t pid;
- int pidfd;
-
int state = ffd_atomic_load(&system_forkfd_state, FFD_ATOMIC_RELAXED);
if (state == 0) {
state = detect_clone_pidfd_support();
ffd_atomic_store(&system_forkfd_state, state, FFD_ATOMIC_RELAXED);
}
+ return state;
+}
+
+static int system_forkfd_pidfd_set_flags(int pidfd, int flags)
+{
+ if ((flags & FFD_CLOEXEC) == 0) {
+ /* pidfd defaults to O_CLOEXEC */
+ fcntl(pidfd, F_SETFD, 0);
+ }
+ if (flags & FFD_NONBLOCK)
+ fcntl(pidfd, F_SETFL, fcntl(pidfd, F_GETFL) | O_NONBLOCK);
+ return pidfd;
+}
+
+int system_vforkfd(int flags, pid_t *ppid, int (*childFn)(void *), void *token, int *system)
+{
+ __attribute__((aligned(64))) char childStack[SIGSTKSZ];
+ pid_t pid;
+ int pidfd;
+ unsigned long cloneflags = CLONE_PIDFD | CLONE_VFORK | CLONE_VM | SIGCHLD;
+
+ int state = system_forkfd_availability();
+ if (state < 0) {
+ *system = 0;
+ return state;
+ }
+ *system = 1;
+
+ pid = clone(childFn, childStack + sizeof(childStack), cloneflags, token, &pidfd, NULL, NULL);
+ if (pid < 0)
+ return pid;
+ if (ppid)
+ *ppid = pid;
+ return system_forkfd_pidfd_set_flags(pidfd, flags);
+}
+
+int system_forkfd(int flags, pid_t *ppid, int *system)
+{
+ pid_t pid;
+ int pidfd;
+
+ int state = system_forkfd_availability();
if (state < 0) {
*system = 0;
return state;
@@ -160,13 +202,7 @@ int system_forkfd(int flags, pid_t *ppid, int *system)
}
/* parent process */
- if ((flags & FFD_CLOEXEC) == 0) {
- /* pidfd defaults to O_CLOEXEC */
- fcntl(pidfd, F_SETFD, 0);
- }
- if (flags & FFD_NONBLOCK)
- fcntl(pidfd, F_SETFL, fcntl(pidfd, F_GETFL) | O_NONBLOCK);
- return pidfd;
+ return system_forkfd_pidfd_set_flags(pidfd, flags);
}
int system_forkfd_wait(int ffd, struct forkfd_info *info, int ffdoptions, struct rusage *rusage)
@@ -184,10 +220,9 @@ int system_forkfd_wait(int ffd, struct forkfd_info *info, int ffdoptions, struct
options |= WNOHANG;
}
+ si.si_status = si.si_code = 0;
ret = sys_waitid(P_PIDFD, ffd, &si, options, rusage);
- if (ret == -1 && errno == ECHILD) {
- errno = EWOULDBLOCK;
- } else if (ret == 0 && info) {
+ if (info) {
info->code = si.si_code;
info->status = si.si_status;
}
diff --git a/src/3rdparty/forkfd/qt_attribution.json b/src/3rdparty/forkfd/qt_attribution.json
index ebbb19c718..1b84779133 100644
--- a/src/3rdparty/forkfd/qt_attribution.json
+++ b/src/3rdparty/forkfd/qt_attribution.json
@@ -3,8 +3,8 @@
"Name": "forkfd",
"QDocModule": "qtcore",
"QtUsage": "Used on most Unix platforms in Qt Core.",
- "Files": "No upstream; treat as final",
- "Files": "forkfd.c forkfd.h forkfd_gcc.h",
+ "Comment": "No upstream; treat as final",
+ "Files": [ "forkfd.c", "forkfd.h", "forkfd_gcc.h" ],
"License": "MIT License",
"LicenseId": "MIT",