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>
This commit is contained in:
Thiago Macieira 2019-08-10 08:13:14 -07:00
parent 8784ab7ba8
commit 1b9274573e
2 changed files with 170 additions and 109 deletions

View File

@ -59,9 +59,6 @@
# define HAVE_PIPE2 1 # define HAVE_PIPE2 1
# endif # endif
#endif #endif
#if defined(__FreeBSD__) && __FreeBSD__ >= 9
# include <sys/procdesc.h>
#endif
#if _POSIX_VERSION-0 >= 200809L || _XOPEN_VERSION-0 >= 500 #if _POSIX_VERSION-0 >= 200809L || _XOPEN_VERSION-0 >= 500
# define HAVE_WAITID 1 # define HAVE_WAITID 1
@ -95,6 +92,10 @@
#include "forkfd_atomic.h" #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_SMALL_ARRAY 16
#define CHILDREN_IN_BIG_ARRAY 256 #define CHILDREN_IN_BIG_ARRAY 256
#define sizeofarray(array) (sizeof(array)/sizeof(array[0])) #define sizeofarray(array) (sizeof(array)/sizeof(array[0]))
@ -446,6 +447,37 @@ static void ignore_sigpipe()
#endif #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() static void forkfd_initialize()
{ {
#if defined(HAVE_BROKEN_WAITID) #if defined(HAVE_BROKEN_WAITID)
@ -487,44 +519,15 @@ static void forkfd_initialize()
ignore_sigpipe(); ignore_sigpipe();
#endif #endif
#ifndef __GNUC__ #ifdef __GNUC__
(void) cleanup; /* suppress unused static function warning */
#else
atexit(cleanup); atexit(cleanup);
#endif #endif
ffd_atomic_store(&forkfd_status, 1, FFD_ATOMIC_RELAXED); 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) static int create_pipe(int filedes[], int flags)
{ {
int ret = -1; int ret = -1;
@ -563,55 +566,6 @@ static int create_pipe(int filedes[], int flags)
return ret; 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 #ifndef FORKFD_NO_FORKFD
/** /**
* @brief forkfd returns a file descriptor representing a child process * @brief forkfd returns a file descriptor representing a child process
@ -659,11 +613,9 @@ int forkfd(int flags, pid_t *ppid)
int efd; int efd;
#endif #endif
if (system_has_forkfd) { fd = system_forkfd(flags, ppid, &ret);
ret = system_forkfd(flags, ppid); if (ret)
if (system_has_forkfd) return fd;
return ret;
}
(void) pthread_once(&forkfd_initialization, forkfd_initialize); (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; /* we can only do work if we have a way to start the child in stopped mode;
* otherwise, we have a major race condition. */ * otherwise, we have a major race condition. */
assert(!system_has_forkfd); assert(!system_has_forkfd());
(void) pthread_once(&forkfd_initialization, forkfd_initialize); (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; struct pipe_payload payload;
int ret; int ret;
if (system_has_forkfd) { if (system_has_forkfd())
#if defined(__FreeBSD__) && __FreeBSD__ >= 9 return system_forkfd_wait(ffd, info, rusage);
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)); ret = read(ffd, &payload, sizeof(payload));
if (ret == -1) if (ret == -1)
@ -884,3 +819,28 @@ int forkfd_close(int ffd)
{ {
return close(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

101
src/3rdparty/forkfd/forkfd_freebsd.c vendored Normal file
View File

@ -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;
}