diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2019-08-10 08:13:14 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2019-08-20 16:57:02 -0700 |
commit | 1b9274573e204ed24ef41eb0dc273bb7aa0be6f2 (patch) | |
tree | b6fc1304f62c40ece8b796a09e71f59ae2480fa9 | |
parent | 8784ab7ba8718c7254a774e3a487950cb77cd5ec (diff) |
forkfd: move the FreeBSD system implementation to a separate file
Simplifies the code a bit and will be helpful when I add the Linux
equivalent.
Change-Id: Iec9c051acd73484c8d94fffd15b99879dc269db9
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rw-r--r-- | src/3rdparty/forkfd/forkfd.c | 178 | ||||
-rw-r--r-- | src/3rdparty/forkfd/forkfd_freebsd.c | 101 |
2 files changed, 170 insertions, 109 deletions
diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index 17a9b23581..e4f3bd85de 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -59,9 +59,6 @@ # 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 @@ -95,6 +92,10 @@ #include "forkfd_atomic.h" +static int system_has_forkfd(void); +static int system_forkfd(int flags, pid_t *ppid, int *system); +static int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage); + #define CHILDREN_IN_SMALL_ARRAY 16 #define CHILDREN_IN_BIG_ARRAY 256 #define sizeofarray(array) (sizeof(array)/sizeof(array[0])) @@ -446,6 +447,37 @@ static void ignore_sigpipe() #endif } +#if defined(__GNUC__) && (!defined(__FreeBSD__) || __FreeBSD__ < 10) +__attribute((destructor, unused)) static void cleanup(); +#endif + +static void cleanup() +{ + BigArray *array; + /* This function is not thread-safe! + * It must only be called when the process is shutting down. + * At shutdown, we expect no one to be calling forkfd(), so we don't + * need to be thread-safe with what is done there. + * + * But SIGCHLD might be delivered to any thread, including this one. + * There's no way to prevent that. The correct solution would be to + * cooperatively delete. We don't do that. + */ + if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 0) + return; + + /* notify the handler that we're no longer in operation */ + ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED); + + /* free any arrays we might have */ + array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE); + while (array != NULL) { + BigArray *next = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE); + free(array); + array = next; + } +} + static void forkfd_initialize() { #if defined(HAVE_BROKEN_WAITID) @@ -487,44 +519,15 @@ static void forkfd_initialize() ignore_sigpipe(); #endif -#ifndef __GNUC__ +#ifdef __GNUC__ + (void) cleanup; /* suppress unused static function warning */ +#else atexit(cleanup); #endif ffd_atomic_store(&forkfd_status, 1, FFD_ATOMIC_RELAXED); } -#ifdef __GNUC__ -__attribute((destructor, unused)) static void cleanup(); -#endif - -static void cleanup() -{ - BigArray *array; - /* This function is not thread-safe! - * It must only be called when the process is shutting down. - * At shutdown, we expect no one to be calling forkfd(), so we don't - * need to be thread-safe with what is done there. - * - * But SIGCHLD might be delivered to any thread, including this one. - * There's no way to prevent that. The correct solution would be to - * cooperatively delete. We don't do that. - */ - if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 0) - return; - - /* notify the handler that we're no longer in operation */ - ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED); - - /* free any arrays we might have */ - array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE); - while (array != NULL) { - BigArray *next = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE); - free(array); - array = next; - } -} - static int create_pipe(int filedes[], int flags) { int ret = -1; @@ -563,55 +566,6 @@ 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) -{ - (void)flags; - (void)ppid; - return -1; -} -#endif - #ifndef FORKFD_NO_FORKFD /** * @brief forkfd returns a file descriptor representing a child process @@ -659,11 +613,9 @@ int forkfd(int flags, pid_t *ppid) int efd; #endif - if (system_has_forkfd) { - ret = system_forkfd(flags, ppid); - if (system_has_forkfd) - return ret; - } + fd = system_forkfd(flags, ppid, &ret); + if (ret) + return fd; (void) pthread_once(&forkfd_initialization, forkfd_initialize); @@ -788,7 +740,7 @@ int spawnfd(int flags, pid_t *ppid, const char *path, const posix_spawn_file_act /* we can only do work if we have a way to start the child in stopped mode; * otherwise, we have a major race condition. */ - assert(!system_has_forkfd); + assert(!system_has_forkfd()); (void) pthread_once(&forkfd_initialization, forkfd_initialize); @@ -846,25 +798,8 @@ int forkfd_wait(int ffd, struct 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 - } + if (system_has_forkfd()) + return system_forkfd_wait(ffd, info, rusage); ret = read(ffd, &payload, sizeof(payload)); if (ret == -1) @@ -884,3 +819,28 @@ int forkfd_close(int ffd) { return close(ffd); } + +#if defined(__FreeBSD__) && __FreeBSD__ >= 9 +# include "forkfd_freebsd.c" +#else +int system_has_forkfd() +{ + return 0; +} + +int system_forkfd(int flags, pid_t *ppid, int *system) +{ + (void)flags; + (void)ppid; + *system = 0; + return -1; +} + +int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) +{ + (void)ffd; + (void)info; + (void)rusage; + return -1; +} +#endif diff --git a/src/3rdparty/forkfd/forkfd_freebsd.c b/src/3rdparty/forkfd/forkfd_freebsd.c new file mode 100644 index 0000000000..77ce3fcfad --- /dev/null +++ b/src/3rdparty/forkfd/forkfd_freebsd.c @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Intel Corporation. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#include "forkfd.h" + +#include <sys/types.h> +#include <sys/procdesc.h> + +#include "forkfd_atomic.h" + +#if __FreeBSD__ >= 10 +/* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option + * anymore and can't be disabled. */ +static ffd_atomic_int system_forkfd_state = FFD_ATOMIC_INIT(1); +#else +static ffd_atomic_int system_forkfd_state = FFD_ATOMIC_INIT(0); +#endif + +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) +{ + int ret; + pid_t pid; + + int state = ffd_atomic_load(&system_forkfd_state, FFD_ATOMIC_RELAXED); + *system = 0; + if (state < 0) + return -1; + + pid = pdfork(&ret, PD_DAEMON); +# if __FreeBSD__ == 9 + if (state == 0 && pid != 0) { + /* Parent process: remember whether PROCDESC was compiled into the kernel */ + state = (pid == -1 && errno == ENOSYS) ? -1 : 1; + ffd_atomic_store(&system_forkfd_state, state, FFD_ATOMIC_RELAXED); + } + if (state < 0) + return -1; +# endif + *system = 1; + if (__builtin_expect(pid == -1, 0)) + 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; +} + +int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) +{ + pid_t pid; + int status; + int options = WEXITED; + + int 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; +} |