From 2eda6464ad1b5898d063da2e48dacd14cdb6dfb0 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 9 Jul 2025 21:27:19 +0800 Subject: [PATCH] Processing (Linux): use `posix_spawnp` for better performance --- src/common/processing_linux.c | 76 +++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/src/common/processing_linux.c b/src/common/processing_linux.c index 770ed60b6..e4ba9bd37 100644 --- a/src/common/processing_linux.c +++ b/src/common/processing_linux.c @@ -11,6 +11,7 @@ #include #include #include +#include #if defined(__FreeBSD__) || defined(__APPLE__) #include @@ -33,6 +34,10 @@ #include #endif +#ifndef environ +extern char** environ; +#endif + enum { FF_PIPE_BUFSIZ = 8192 }; static inline int ffPipe2(int* fds, int flags) @@ -56,28 +61,39 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use if(ffPipe2(pipes, O_CLOEXEC) == -1) return "pipe() failed"; - pid_t childPid = fork(); - if(childPid == -1) + posix_spawn_file_actions_t file_actions; + posix_spawn_file_actions_init(&file_actions); + posix_spawn_file_actions_adddup2(&file_actions, pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO); + posix_spawn_file_actions_addopen(&file_actions, useStdErr ? STDOUT_FILENO : STDERR_FILENO, "/dev/null", O_WRONLY, 0); + pid_t childPid = -1; + + char* oldLang = NULL; + int langIndex = -1; + for (int i = 0; environ[i] != NULL; i++) + { + if (ffStrStartsWith(environ[i], "LANG=")) + { + oldLang = environ[i]; + environ[i] = (char*) "LANG=C"; + langIndex = i; + break; + } + } + + int ret = posix_spawnp(&childPid, argv[0], &file_actions, NULL, argv, environ); + + if (oldLang) + environ[langIndex] = oldLang; + + posix_spawn_file_actions_destroy(&file_actions); + + close(pipes[1]); + if (ret != 0) { close(pipes[0]); - close(pipes[1]); - return "fork() failed"; + return "posix_spawnp() failed"; } - //Child - if(childPid == 0) - { - int nullFile = open("/dev/null", O_WRONLY | O_CLOEXEC); - dup2(pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO); - dup2(nullFile, useStdErr ? STDOUT_FILENO : STDERR_FILENO); - setenv("LANG", "C", 1); - execvp(argv[0], argv); - _exit(127); - } - - //Parent - close(pipes[1]); - int FF_AUTO_CLOSE_FD childPipeFd = pipes[0]; char str[FF_PIPE_BUFSIZ]; @@ -86,20 +102,30 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use if (timeout >= 0) { struct pollfd pollfd = { childPipeFd, POLLIN, 0 }; - if (poll(&pollfd, 1, timeout) == 0) + int pollret = poll(&pollfd, 1, timeout); + if (pollret == 0) { kill(childPid, SIGTERM); waitpid(childPid, NULL, 0); return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)"; } - else if (errno == EINTR) + else if (pollret < 0) { - // The child process has been terminated. See `chldSignalHandler` in `common/init.c` - if (waitpid(childPid, NULL, WNOHANG) == childPid) + if (errno == EINTR) { - // Read remaining data from the pipe - fcntl(childPipeFd, F_SETFL, O_CLOEXEC | O_NONBLOCK); - childPid = -1; + // The child process has been terminated. See `chldSignalHandler` in `common/init.c` + if (waitpid(childPid, NULL, WNOHANG) == childPid) + { + // Read remaining data from the pipe + fcntl(childPipeFd, F_SETFL, O_CLOEXEC | O_NONBLOCK); + childPid = -1; + } + } + else + { + kill(childPid, SIGTERM); + waitpid(childPid, NULL, 0); + return "poll(&pollfd, 1, timeout) error"; } } else if (pollfd.revents & POLLERR)