mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-27 15:52:00 -08:00
Use posix_spawn if possible.
posix_spawn is less error-prone than vfork + execve, and can make better use of system-specific enhancements like 'clone' on Linux. Use it if we don't need to configure a pseudoterminal. Backported from commita60053f836. Unlike that commit, only define USABLE_POSIX_SPAWN on macOS, because there posix_spawn is much faster than vfork. Don't merge to master. * configure.ac (HAVE_SPAWN_H, HAVE_POSIX_SPAWN) (HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR) (HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) (HAVE_POSIX_SPAWNATTR_SETFLAGS, HAVE_DECL_POSIX_SPAWN_SETSID): New configuration variables. * src/callproc.c (USABLE_POSIX_SPAWN): New configuration macro. (emacs_posix_spawn_init_actions) (emacs_posix_spawn_init_attributes, emacs_posix_spawn_init): New helper functions. (emacs_spawn): Use posix_spawn if possible. (cherry picked from commita60053f836)
This commit is contained in:
parent
a56dd60d2f
commit
cc4edea872
2 changed files with 206 additions and 1 deletions
17
configure.ac
17
configure.ac
|
|
@ -4746,6 +4746,23 @@ dnl AC_CHECK_FUNCS_ONCE wouldn’t be right for snprintf, which needs
|
||||||
dnl the current CFLAGS etc.
|
dnl the current CFLAGS etc.
|
||||||
AC_CHECK_FUNCS(snprintf)
|
AC_CHECK_FUNCS(snprintf)
|
||||||
|
|
||||||
|
dnl posix_spawn. The chdir and setsid functionality is relatively
|
||||||
|
dnl recent, so we check for it specifically.
|
||||||
|
AC_CHECK_HEADERS([spawn.h])
|
||||||
|
AC_SUBST([HAVE_SPAWN_H])
|
||||||
|
AC_CHECK_FUNCS([posix_spawn \
|
||||||
|
posix_spawn_file_actions_addchdir \
|
||||||
|
posix_spawn_file_actions_addchdir_np \
|
||||||
|
posix_spawnattr_setflags])
|
||||||
|
AC_SUBST([HAVE_POSIX_SPAWN])
|
||||||
|
AC_SUBST([HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR])
|
||||||
|
AC_SUBST([HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP])
|
||||||
|
AC_SUBST([HAVE_POSIX_SPAWNATTR_SETFLAGS])
|
||||||
|
AC_CHECK_DECLS([POSIX_SPAWN_SETSID], [], [], [[
|
||||||
|
#include <spawn.h>
|
||||||
|
]])
|
||||||
|
AC_SUBST([HAVE_DECL_POSIX_SPAWN_SETSID])
|
||||||
|
|
||||||
dnl Check for glib. This differs from other library checks in that
|
dnl Check for glib. This differs from other library checks in that
|
||||||
dnl Emacs need not link to glib unless some other library is already
|
dnl Emacs need not link to glib unless some other library is already
|
||||||
dnl linking to glib. Although glib provides no facilities that Emacs
|
dnl linking to glib. Although glib provides no facilities that Emacs
|
||||||
|
|
|
||||||
190
src/callproc.c
190
src/callproc.c
|
|
@ -28,6 +28,21 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
/* In order to be able to use `posix_spawn', it needs to support some
|
||||||
|
variant of `chdir' as well as `setsid'. */
|
||||||
|
#if defined DARWIN_OS \
|
||||||
|
&& defined HAVE_SPAWN_H && defined HAVE_POSIX_SPAWN \
|
||||||
|
&& defined HAVE_POSIX_SPAWNATTR_SETFLAGS \
|
||||||
|
&& (defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR \
|
||||||
|
|| defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) \
|
||||||
|
&& defined HAVE_DECL_POSIX_SPAWN_SETSID \
|
||||||
|
&& HAVE_DECL_POSIX_SPAWN_SETSID == 1
|
||||||
|
# include <spawn.h>
|
||||||
|
# define USABLE_POSIX_SPAWN 1
|
||||||
|
#else
|
||||||
|
# define USABLE_POSIX_SPAWN 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
|
|
||||||
#ifdef SETUP_SLAVE_PTY
|
#ifdef SETUP_SLAVE_PTY
|
||||||
|
|
@ -1247,6 +1262,130 @@ child_setup (int in, int out, int err, char **new_argv, char **env,
|
||||||
#endif /* not WINDOWSNT */
|
#endif /* not WINDOWSNT */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USABLE_POSIX_SPAWN
|
||||||
|
|
||||||
|
/* Set up ACTIONS and ATTRIBUTES for `posix_spawn'. Return an error
|
||||||
|
number. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
emacs_posix_spawn_init_actions (posix_spawn_file_actions_t *actions,
|
||||||
|
int std_in, int std_out, int std_err,
|
||||||
|
const char *cwd)
|
||||||
|
{
|
||||||
|
int error = posix_spawn_file_actions_init (actions);
|
||||||
|
if (error != 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = posix_spawn_file_actions_adddup2 (actions, std_in,
|
||||||
|
STDIN_FILENO);
|
||||||
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = posix_spawn_file_actions_adddup2 (actions, std_out,
|
||||||
|
STDOUT_FILENO);
|
||||||
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = posix_spawn_file_actions_adddup2 (actions,
|
||||||
|
std_err < 0 ? std_out
|
||||||
|
: std_err,
|
||||||
|
STDERR_FILENO);
|
||||||
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error =
|
||||||
|
#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR
|
||||||
|
posix_spawn_file_actions_addchdir
|
||||||
|
#else
|
||||||
|
posix_spawn_file_actions_addchdir_np
|
||||||
|
#endif
|
||||||
|
(actions, cwd);
|
||||||
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (error != 0)
|
||||||
|
posix_spawn_file_actions_destroy (actions);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
emacs_posix_spawn_init_attributes (posix_spawnattr_t *attributes)
|
||||||
|
{
|
||||||
|
int error = posix_spawnattr_init (attributes);
|
||||||
|
if (error != 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = posix_spawnattr_setflags (attributes,
|
||||||
|
POSIX_SPAWN_SETSID
|
||||||
|
| POSIX_SPAWN_SETSIGDEF
|
||||||
|
| POSIX_SPAWN_SETSIGMASK);
|
||||||
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
sigset_t sigdefault;
|
||||||
|
sigemptyset (&sigdefault);
|
||||||
|
|
||||||
|
#ifdef DARWIN_OS
|
||||||
|
/* Work around a macOS bug, where SIGCHLD is apparently
|
||||||
|
delivered to a vforked child instead of to its parent. See:
|
||||||
|
https://lists.gnu.org/r/emacs-devel/2017-05/msg00342.html
|
||||||
|
*/
|
||||||
|
sigaddset (&sigdefault, SIGCHLD);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sigaddset (&sigdefault, SIGINT);
|
||||||
|
sigaddset (&sigdefault, SIGQUIT);
|
||||||
|
#ifdef SIGPROF
|
||||||
|
sigaddset (&sigdefault, SIGPROF);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Emacs ignores SIGPIPE, but the child should not. */
|
||||||
|
sigaddset (&sigdefault, SIGPIPE);
|
||||||
|
/* Likewise for SIGPROF. */
|
||||||
|
#ifdef SIGPROF
|
||||||
|
sigaddset (&sigdefault, SIGPROF);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
error = posix_spawnattr_setsigdefault (attributes, &sigdefault);
|
||||||
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Stop blocking SIGCHLD in the child. */
|
||||||
|
sigset_t oldset;
|
||||||
|
error = pthread_sigmask (SIG_SETMASK, NULL, &oldset);
|
||||||
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
error = posix_spawnattr_setsigmask (attributes, &oldset);
|
||||||
|
if (error != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (error != 0)
|
||||||
|
posix_spawnattr_destroy (attributes);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
emacs_posix_spawn_init (posix_spawn_file_actions_t *actions,
|
||||||
|
posix_spawnattr_t *attributes, int std_in,
|
||||||
|
int std_out, int std_err, const char *cwd)
|
||||||
|
{
|
||||||
|
int error = emacs_posix_spawn_init_actions (actions, std_in,
|
||||||
|
std_out, std_err, cwd);
|
||||||
|
if (error != 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = emacs_posix_spawn_init_attributes (attributes);
|
||||||
|
if (error != 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Start a new asynchronous subprocess. If successful, return zero
|
/* Start a new asynchronous subprocess. If successful, return zero
|
||||||
and store the process identifier of the new process in *NEWPID.
|
and store the process identifier of the new process in *NEWPID.
|
||||||
Use STDIN, STDOUT, and STDERR as standard streams for the new
|
Use STDIN, STDOUT, and STDERR as standard streams for the new
|
||||||
|
|
@ -1266,10 +1405,58 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err,
|
||||||
char **argv, char **envp, const char *cwd,
|
char **argv, char **envp, const char *cwd,
|
||||||
const char *pty, const sigset_t *oldset)
|
const char *pty, const sigset_t *oldset)
|
||||||
{
|
{
|
||||||
|
#if USABLE_POSIX_SPAWN
|
||||||
|
/* Prefer the simpler `posix_spawn' if available. `posix_spawn'
|
||||||
|
doesn't yet support setting up pseudoterminals, so we fall back
|
||||||
|
to `vfork' if we're supposed to use a pseudoterminal. */
|
||||||
|
|
||||||
|
bool use_posix_spawn = pty == NULL;
|
||||||
|
|
||||||
|
posix_spawn_file_actions_t actions;
|
||||||
|
posix_spawnattr_t attributes;
|
||||||
|
|
||||||
|
if (use_posix_spawn)
|
||||||
|
{
|
||||||
|
/* Initialize optional attributes before blocking. */
|
||||||
|
int error
|
||||||
|
= emacs_posix_spawn_init (&actions, &attributes, std_in,
|
||||||
|
std_out, std_err, cwd);
|
||||||
|
if (error != 0)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int pid;
|
int pid;
|
||||||
|
int vfork_error;
|
||||||
|
|
||||||
eassert (input_blocked_p ());
|
eassert (input_blocked_p ());
|
||||||
|
|
||||||
|
#if USABLE_POSIX_SPAWN
|
||||||
|
if (use_posix_spawn)
|
||||||
|
{
|
||||||
|
vfork_error = posix_spawn (&pid, argv[0], &actions, &attributes,
|
||||||
|
argv, envp);
|
||||||
|
if (vfork_error != 0)
|
||||||
|
pid = -1;
|
||||||
|
|
||||||
|
int error = posix_spawn_file_actions_destroy (&actions);
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
errno = error;
|
||||||
|
emacs_perror ("posix_spawn_file_actions_destroy");
|
||||||
|
}
|
||||||
|
|
||||||
|
error = posix_spawnattr_destroy (&attributes);
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
errno = error;
|
||||||
|
emacs_perror ("posix_spawnattr_destroy");
|
||||||
|
}
|
||||||
|
|
||||||
|
goto fork_done;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef WINDOWSNT
|
#ifndef WINDOWSNT
|
||||||
/* vfork, and prevent local vars from being clobbered by the vfork. */
|
/* vfork, and prevent local vars from being clobbered by the vfork. */
|
||||||
pid_t *volatile newpid_volatile = newpid;
|
pid_t *volatile newpid_volatile = newpid;
|
||||||
|
|
@ -1413,8 +1600,9 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err,
|
||||||
|
|
||||||
/* Back in the parent process. */
|
/* Back in the parent process. */
|
||||||
|
|
||||||
int vfork_error = pid < 0 ? errno : 0;
|
vfork_error = pid < 0 ? errno : 0;
|
||||||
|
|
||||||
|
fork_done:
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
{
|
{
|
||||||
eassert (0 < vfork_error);
|
eassert (0 < vfork_error);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue