diff options
Diffstat (limited to 'src/3rdparty/forkfd')
-rw-r--r-- | src/3rdparty/forkfd/forkfd.c | 188 | ||||
-rw-r--r-- | src/3rdparty/forkfd/forkfd.h | 1 | ||||
-rw-r--r-- | src/3rdparty/forkfd/forkfd_c11.h | 4 | ||||
-rw-r--r-- | src/3rdparty/forkfd/forkfd_freebsd.c | 2 | ||||
-rw-r--r-- | src/3rdparty/forkfd/forkfd_linux.c | 65 | ||||
-rw-r--r-- | src/3rdparty/forkfd/qt_attribution.json | 4 |
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", |