summaryrefslogtreecommitdiff
path: root/src/process.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2012-09-23 01:44:20 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2012-09-23 01:44:20 -0700
commit4d7e6e51dd4acecff466a28d958c50f34fc130b8 (patch)
tree5e340d48fb69f9a40a4304cc94db2006caefe51f /src/process.c
parent8f4635e97e1587c4026ec83fc1bd9f8954775915 (diff)
downloademacs-4d7e6e51dd4acecff466a28d958c50f34fc130b8.tar.gz
Simplify and avoid signal-handling races.
* nt/inc/ms-w32.h (emacs_raise): New macro. * src/alloc.c (die): * src/sysdep.c (emacs_abort) [HAVE_NTGUI]: Avoid recursive loop if there's a fatal error in the function itself. * src/atimer.c (pending_atimers): * src/blockinput.h: Don't include "atimer.h"; no longer needed. (interrupt_input_pending): Remove. All uses removed. pending_signals now counts both atimers and ordinary interrupts. This is less racy than having three separate pending-signal flags. (block_input, unblock_input, totally_unblock_input, unblock_input_to) (input_blocked_p): Rename from their upper-case counterparts BLOCK_INPUT, UNBLOCK_INPUT, TOTALLY_UNBLOCK_INPUT, UNBLOCK_INPUT_TO, INPUT_BLOCKED_P, and turn into functions. All uses changed. This makes it easier to access volatile variables more accurately. (BLOCK_INPUT_RESIGNAL): Remove. All uses replaced by unblock_input (). (input_blocked_p): Prefer this to 'interrupt_input_blocked', as that's more reliable if the code is buggy and sets interrupt_input_blocked to a negative value. All uses changed. * src/atimer.c (deliver_alarm_signal): Remove. No need to deliver this to the parent; any thread can handle this signal now. All uses replaced by underlying handler. * src/atimer.c (turn_on_atimers): * src/dispnew.c (handle_window_change_signal): * src/emacs.c (handle_danger_signal): * src/keyboard.c (kbd_buffer_get_event): Don't reestablish signal handler; not needed with sigaction. * src/blockinput.h (UNBLOCK_INPUT_TO, TOTALLY_UNBLOCK_INPUT) (UNBLOCK_INPUT_TO): Rework to avoid unnecessary accesses to volatile variables. (UNBLOCK_INPUT_TO): Now a function. (totally_unblock_input, unblock_input): New decls. * src/data.c (handle_arith_signal, deliver_arith_signal): Move to sysdep.c (init_data): Remove. Necessary stuff now done in init_signal. * src/emacs.c, src/xdisp.c: Include "atimer.h", since we invoke atimer functions. * src/emacs.c (handle_fatal_signal, deliver_fatal_signal): Move to sysdep.c. (fatal_error_code): Remove; no longer needed. (terminate_due_to_signal): Rename from fatal_error_backtrace, since it doesn't always backtrace. All uses changed. No need to reset signal to default, since sigaction and/or die does that for us now. Use emacs_raise (FOO), not kill (getpid (), FOO). (main): Check more-accurately whether we're dumping. Move fatal-error setup to sysdep.c * src/floatfns.c: Do not include "syssignal.h"; no longer needed. * src/gtkutil.c (xg_get_file_name, xg_get_font): Remove no-longer-needed signal-mask manipulation. * src/keyboard.c, src/process.c (POLL_FOR_INPUT): Don't depend on USE_ASYNC_EVENTS, a symbol that is never defined. * src/keyboard.c (read_avail_input): Remove. All uses replaced by gobble_input. (Ftop_level): Use TOTALLY_UNBLOCK_INPUT rather than open code. (kbd_buffer_store_event_hold, gobble_input): (record_asynch_buffer_change) [USABLE_SIGIO]: (store_user_signal_events): No need to mess with signal mask. (gobble_input): If blocking input and there are terminals, simply set pending_signals to 1 and return. All hooks changed to not worry about whether input is blocked. (process_pending_signals): Clear pending_signals before processing them, in case a signal comes in while we're processing. By convention callers now test pending_signals before calling us. (UNBLOCK_INPUT_TO, unblock_input, totally_unblock_input): New functions, to support changes to blockinput.h. (handle_input_available_signal): Now extern. (reinvoke_input_signal): Remove. All uses replaced by handle_async_input. (quit_count): Now volatile, since a signal handler uses it. (handle_interrupt): Now takes bool IN_SIGNAL_HANDLER as arg. All callers changed. Block SIGINT only if not already blocked. Clear sigmask reliably, even if Fsignal returns, which it can. Omit unnecessary accesses to volatile var. (quit_throw_to_read_char): No need to restore sigmask. * src/keyboard.c (gobble_input, handle_user_signal): * src/process.c (wait_reading_process_output): Call signal-handling code rather than killing ourselves. * src/lisp.h: Include <float.h>, for... (IEEE_FLOATING_POINT): New macro, moved here to avoid duplication. (pending_signals): Now volatile. (syms_of_data): Now const if IEEE floating point. (handle_input_available_signal) [USABLE_SIGIO]: (terminate_due_to_signal, record_child_status_change): New decls. * src/process.c (create_process): Avoid disaster if memory is exhausted while we're processing a vfork, by tightening the critical section around the vfork. (send_process_frame, process_sent_to, handle_pipe_signal) (deliver_pipe_signal): Remove. No longer needed, as Emacs now ignores SIGPIPE. (send_process): No need for setjmp/longjmp any more, since the SIGPIPE stuff is now gone. Instead, report an error if errno is EPIPE. (record_child_status_change): Now extern. PID and W are now args. Return void, not bool. All callers changed. * src/sysdep.c (wait_debugging) [(BSD_SYSTEM || HPUX) && !defined (__GNU__)]: Remove. All uses removed. This bug should be fixed now in a different way. (wait_for_termination_1): Use waitpid rather than sigsuspend, and record the child status change directly. This avoids the need to futz with the signal mask. (process_fatal_action): Move here from emacs.c. (emacs_sigaction_flags): New function, containing much of what used to be in emacs_sigaction_init. (emacs_sigaction_init): Use it. Block nonfatal system signals that are caught by emacs, to make races less likely. (deliver_process_signal): Rename from handle_on_main_thread. All uses changed. (BACKTRACE_LIMIT_MAX): Now at top level. (thread_backtrace_buffer, threadback_backtrace_pointers): New static vars. (deliver_thread_signal, deliver_fatal_thread_signal): New functions, for more-accurate delivery of thread-specific signals. (handle_fatal_signal, deliver_fatal_signal): Move here from emacs.c. (deliver_arith_signal): Handle in this thread, not in the main thread, since it's triggered by this thread. (maybe_fatal_sig): New function. (init_signals): New arg DUMPING so that we can be more accurate about whether we're dumping. Caller changed. Treat thread-specific signals differently from process-general signals. Block all signals while handling fatal error; that's safer. xsignal from SIGFPE only on non-IEEE hosts, treating it as fatal on IEEE hosts. When batch, ignore SIGHUP, SIGINT, SIGTERM if they were already ignored. Ignore SIGPIPE unless batch. (emacs_backtrace): Output backtrace for the appropriate thread, which is not necessarily the main thread. * src/syssignal.h: Include <stdbool.h>. (emacs_raise): New macro. * src/xterm.c (x_connection_signal): Remove; no longer needed now that we use sigaction. (x_connection_closed): No need to mess with sigmask now. (x_initialize): No need to reset SIGPIPE handler here, since init_signals does this for us now. Fixes: debbugs:12471
Diffstat (limited to 'src/process.c')
-rw-r--r--src/process.c574
1 files changed, 259 insertions, 315 deletions
diff --git a/src/process.c b/src/process.c
index c654369627d..ab215766c07 100644
--- a/src/process.c
+++ b/src/process.c
@@ -255,11 +255,12 @@ static int keyboard_bit_set (SELECT_TYPE *);
static void deactivate_process (Lisp_Object);
static void status_notify (struct Lisp_Process *);
static int read_process_output (Lisp_Object, int);
+static void handle_child_signal (int);
static void create_pty (Lisp_Object);
/* If we support a window system, turn on the code to poll periodically
to detect C-g. It isn't actually used when doing interrupt input. */
-#if defined (HAVE_WINDOW_SYSTEM) && !defined (USE_ASYNC_EVENTS)
+#ifdef HAVE_WINDOW_SYSTEM
#define POLL_FOR_INPUT
#endif
@@ -1610,11 +1611,16 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
int wait_child_setup[2];
#endif
#ifdef SIGCHLD
- sigset_t blocked, procmask;
+ sigset_t blocked;
#endif
/* Use volatile to protect variables from being clobbered by vfork. */
volatile int forkin, forkout;
volatile int pty_flag = 0;
+ volatile Lisp_Object lisp_pty_name = Qnil;
+ volatile Lisp_Object encoded_current_dir;
+#if HAVE_WORKING_VFORK
+ char **volatile save_environ;
+#endif
inchannel = outchannel = -1;
@@ -1640,6 +1646,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
forkin = forkout = -1;
#endif /* not USG, or USG_SUBTTY_WORKS */
pty_flag = 1;
+ lisp_pty_name = build_string (pty_name);
}
else
#endif /* HAVE_PTYS */
@@ -1704,14 +1711,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
XPROCESS (process)->pty_flag = pty_flag;
pset_status (XPROCESS (process), Qrun);
-#ifdef SIGCHLD
- /* Delay interrupts until we have a chance to store
- the new fork's pid in its process structure */
- sigemptyset (&blocked);
- sigaddset (&blocked, SIGCHLD);
- pthread_sigmask (SIG_BLOCK, &blocked, &procmask);
-#endif
-
FD_SET (inchannel, &input_wait_mask);
FD_SET (inchannel, &non_keyboard_wait_mask);
if (inchannel > max_process_desc)
@@ -1729,89 +1728,99 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
error. */
setup_process_coding_systems (process);
- BLOCK_INPUT;
+ encoded_current_dir = ENCODE_FILE (current_dir);
- {
- /* child_setup must clobber environ on systems with true vfork.
- Protect it from permanent change. */
- char **save_environ = environ;
- volatile Lisp_Object encoded_current_dir = ENCODE_FILE (current_dir);
+ block_input ();
+
+#ifdef SIGCHLD
+ /* Block SIGCHLD until we have a chance to store the new fork's
+ pid in its process structure. */
+ sigemptyset (&blocked);
+ sigaddset (&blocked, SIGCHLD);
+ pthread_sigmask (SIG_BLOCK, &blocked, 0);
+#endif
+
+#if HAVE_WORKING_VFORK
+ /* child_setup must clobber environ on systems with true vfork.
+ Protect it from permanent change. */
+ save_environ = environ;
+#endif
#ifndef WINDOWSNT
- pid = vfork ();
- if (pid == 0)
+ pid = vfork ();
+ if (pid == 0)
#endif /* not WINDOWSNT */
- {
- int xforkin = forkin;
- int xforkout = forkout;
+ {
+ int xforkin = forkin;
+ int xforkout = forkout;
- /* Make the pty be the controlling terminal of the process. */
+ /* Make the pty be the controlling terminal of the process. */
#ifdef HAVE_PTYS
- /* First, disconnect its current controlling terminal. */
+ /* First, disconnect its current controlling terminal. */
#ifdef HAVE_SETSID
- /* We tried doing setsid only if pty_flag, but it caused
- process_set_signal to fail on SGI when using a pipe. */
- setsid ();
- /* Make the pty's terminal the controlling terminal. */
- if (pty_flag && xforkin >= 0)
- {
+ /* We tried doing setsid only if pty_flag, but it caused
+ process_set_signal to fail on SGI when using a pipe. */
+ setsid ();
+ /* Make the pty's terminal the controlling terminal. */
+ if (pty_flag && xforkin >= 0)
+ {
#ifdef TIOCSCTTY
- /* We ignore the return value
- because faith@cs.unc.edu says that is necessary on Linux. */
- ioctl (xforkin, TIOCSCTTY, 0);
+ /* We ignore the return value
+ because faith@cs.unc.edu says that is necessary on Linux. */
+ ioctl (xforkin, TIOCSCTTY, 0);
#endif
- }
+ }
#else /* not HAVE_SETSID */
#ifdef USG
- /* It's very important to call setpgrp here and no time
- afterwards. Otherwise, we lose our controlling tty which
- is set when we open the pty. */
- setpgrp ();
+ /* It's very important to call setpgrp here and no time
+ afterwards. Otherwise, we lose our controlling tty which
+ is set when we open the pty. */
+ setpgrp ();
#endif /* USG */
#endif /* not HAVE_SETSID */
#if defined (LDISC1)
- if (pty_flag && xforkin >= 0)
- {
- struct termios t;
- tcgetattr (xforkin, &t);
- t.c_lflag = LDISC1;
- if (tcsetattr (xforkin, TCSANOW, &t) < 0)
- emacs_write (1, "create_process/tcsetattr LDISC1 failed\n", 39);
- }
+ if (pty_flag && xforkin >= 0)
+ {
+ struct termios t;
+ tcgetattr (xforkin, &t);
+ t.c_lflag = LDISC1;
+ if (tcsetattr (xforkin, TCSANOW, &t) < 0)
+ emacs_write (1, "create_process/tcsetattr LDISC1 failed\n", 39);
+ }
#else
#if defined (NTTYDISC) && defined (TIOCSETD)
- if (pty_flag && xforkin >= 0)
- {
- /* Use new line discipline. */
- int ldisc = NTTYDISC;
- ioctl (xforkin, TIOCSETD, &ldisc);
- }
+ if (pty_flag && xforkin >= 0)
+ {
+ /* Use new line discipline. */
+ int ldisc = NTTYDISC;
+ ioctl (xforkin, TIOCSETD, &ldisc);
+ }
#endif
#endif
#ifdef TIOCNOTTY
- /* In 4.3BSD, the TIOCSPGRP bug has been fixed, and now you
- can do TIOCSPGRP only to the process's controlling tty. */
- if (pty_flag)
- {
- /* I wonder: would just ioctl (0, TIOCNOTTY, 0) work here?
- I can't test it since I don't have 4.3. */
- int j = emacs_open ("/dev/tty", O_RDWR, 0);
- if (j >= 0)
- {
- ioctl (j, TIOCNOTTY, 0);
- emacs_close (j);
- }
+ /* In 4.3BSD, the TIOCSPGRP bug has been fixed, and now you
+ can do TIOCSPGRP only to the process's controlling tty. */
+ if (pty_flag)
+ {
+ /* I wonder: would just ioctl (0, TIOCNOTTY, 0) work here?
+ I can't test it since I don't have 4.3. */
+ int j = emacs_open ("/dev/tty", O_RDWR, 0);
+ if (j >= 0)
+ {
+ ioctl (j, TIOCNOTTY, 0);
+ emacs_close (j);
+ }
#ifndef USG
- /* In order to get a controlling terminal on some versions
- of BSD, it is necessary to put the process in pgrp 0
- before it opens the terminal. */
+ /* In order to get a controlling terminal on some versions
+ of BSD, it is necessary to put the process in pgrp 0
+ before it opens the terminal. */
#ifdef HAVE_SETPGID
- setpgid (0, 0);
+ setpgid (0, 0);
#else
- setpgrp (0, 0);
+ setpgrp (0, 0);
#endif
#endif
- }
+ }
#endif /* TIOCNOTTY */
#if !defined (DONT_REOPEN_PTY)
@@ -1823,70 +1832,79 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
both HAVE_SETSID and TIOCSCTTY are defined. */
/* Now close the pty (if we had it open) and reopen it.
This makes the pty the controlling terminal of the subprocess. */
- if (pty_flag)
- {
+ if (pty_flag)
+ {
- /* I wonder if emacs_close (emacs_open (pty_name, ...))
- would work? */
- if (xforkin >= 0)
- emacs_close (xforkin);
- xforkout = xforkin = emacs_open (pty_name, O_RDWR, 0);
+ /* I wonder if emacs_close (emacs_open (pty_name, ...))
+ would work? */
+ if (xforkin >= 0)
+ emacs_close (xforkin);
+ xforkout = xforkin = emacs_open (pty_name, O_RDWR, 0);
- if (xforkin < 0)
- {
- emacs_write (1, "Couldn't open the pty terminal ", 31);
- emacs_write (1, pty_name, strlen (pty_name));
- emacs_write (1, "\n", 1);
- _exit (1);
- }
+ if (xforkin < 0)
+ {
+ emacs_write (1, "Couldn't open the pty terminal ", 31);
+ emacs_write (1, pty_name, strlen (pty_name));
+ emacs_write (1, "\n", 1);
+ _exit (1);
+ }
- }
+ }
#endif /* not DONT_REOPEN_PTY */
#ifdef SETUP_SLAVE_PTY
- if (pty_flag)
- {
- SETUP_SLAVE_PTY;
- }
+ if (pty_flag)
+ {
+ SETUP_SLAVE_PTY;
+ }
#endif /* SETUP_SLAVE_PTY */
#ifdef AIX
- /* On AIX, we've disabled SIGHUP above once we start a child on a pty.
- Now reenable it in the child, so it will die when we want it to. */
- if (pty_flag)
- signal (SIGHUP, SIG_DFL);
+ /* On AIX, we've disabled SIGHUP above once we start a child on a pty.
+ Now reenable it in the child, so it will die when we want it to. */
+ if (pty_flag)
+ signal (SIGHUP, SIG_DFL);
#endif
#endif /* HAVE_PTYS */
- signal (SIGINT, SIG_DFL);
- signal (SIGQUIT, SIG_DFL);
- /* GConf causes us to ignore SIGPIPE, make sure it is restored
- in the child. */
- signal (SIGPIPE, SIG_DFL);
+ signal (SIGINT, SIG_DFL);
+ signal (SIGQUIT, SIG_DFL);
+
+ /* Emacs ignores SIGPIPE, but the child should not. */
+ signal (SIGPIPE, SIG_DFL);
#ifdef SIGCHLD
/* Stop blocking signals in the child. */
- pthread_sigmask (SIG_SETMASK, &procmask, 0);
+ pthread_sigmask (SIG_SETMASK, &empty_mask, 0);
#endif
- if (pty_flag)
- child_setup_tty (xforkout);
+ if (pty_flag)
+ child_setup_tty (xforkout);
#ifdef WINDOWSNT
- pid = child_setup (xforkin, xforkout, xforkout,
- new_argv, 1, encoded_current_dir);
+ pid = child_setup (xforkin, xforkout, xforkout,
+ new_argv, 1, encoded_current_dir);
#else /* not WINDOWSNT */
#ifdef FD_CLOEXEC
- emacs_close (wait_child_setup[0]);
+ emacs_close (wait_child_setup[0]);
#endif
- child_setup (xforkin, xforkout, xforkout,
- new_argv, 1, encoded_current_dir);
+ child_setup (xforkin, xforkout, xforkout,
+ new_argv, 1, encoded_current_dir);
#endif /* not WINDOWSNT */
- }
- environ = save_environ;
- }
+ }
+
+ /* Back in the parent process. */
+
+#if HAVE_WORKING_VFORK
+ environ = save_environ;
+#endif
+
+ XPROCESS (process)->pid = pid;
- UNBLOCK_INPUT;
+ /* Stop blocking signals in the parent. */
+#ifdef SIGCHLD
+ pthread_sigmask (SIG_SETMASK, &empty_mask, 0);
+#endif
+ unblock_input ();
- /* This runs in the Emacs process. */
if (pid < 0)
{
if (forkin >= 0)
@@ -1897,7 +1915,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
else
{
/* vfork succeeded. */
- XPROCESS (process)->pid = pid;
#ifdef WINDOWSNT
register_child (pid, inchannel);
@@ -1923,12 +1940,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
if (forkin != forkout && forkout >= 0)
emacs_close (forkout);
-#ifdef HAVE_PTYS
- if (pty_flag)
- pset_tty_name (XPROCESS (process), build_string (pty_name));
- else
-#endif
- pset_tty_name (XPROCESS (process), Qnil);
+ pset_tty_name (XPROCESS (process), lisp_pty_name);
#if !defined (WINDOWSNT) && defined (FD_CLOEXEC)
/* Wait for child_setup to complete in case that vfork is
@@ -1945,11 +1957,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
#endif
}
-#ifdef SIGCHLD
- /* Stop blocking signals in the parent. */
- pthread_sigmask (SIG_SETMASK, &procmask, 0);
-#endif
-
/* Now generate the error if vfork failed. */
if (pid < 0)
report_file_error ("Doing vfork", Qnil);
@@ -3402,9 +3409,9 @@ usage: (make-network-process &rest ARGS) */)
#ifdef HAVE_GETADDRINFO
if (res != &ai)
{
- BLOCK_INPUT;
+ block_input ();
freeaddrinfo (res);
- UNBLOCK_INPUT;
+ unblock_input ();
}
#endif
@@ -4372,7 +4379,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
Otherwise, do pending quit if requested. */
if (read_kbd >= 0)
QUIT;
- else
+ else if (pending_signals)
process_pending_signals ();
/* Exit now if the cell we're waiting for became non-nil. */
@@ -4739,21 +4746,6 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
check_write = 0;
}
-#if 0 /* When polling is used, interrupt_input is 0,
- so get_input_pending should read the input.
- So this should not be needed. */
- /* If we are using polling for input,
- and we see input available, make it get read now.
- Otherwise it might not actually get read for a second.
- And on hpux, since we turn off polling in wait_reading_process_output,
- it might never get read at all if we don't spend much time
- outside of wait_reading_process_output. */
- if (read_kbd && interrupt_input
- && keyboard_bit_set (&Available)
- && input_polling_used ())
- kill (getpid (), SIGALRM);
-#endif
-
/* Check for keyboard input */
/* If there is any, return immediately
to give it higher priority than subprocesses */
@@ -4817,7 +4809,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
if (read_kbd && interrupt_input
&& keyboard_bit_set (&Available) && ! noninteractive)
- kill (getpid (), SIGIO);
+ handle_input_available_signal (SIGIO);
#endif
if (! wait_proc)
@@ -4937,7 +4929,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
pset_status (p, Qfailed);
}
else
- kill (getpid (), SIGCHLD);
+ handle_child_signal (SIGCHLD);
}
#endif /* HAVE_PTYS */
/* If we can detect process termination, don't consider the
@@ -5393,25 +5385,6 @@ read_process_output (Lisp_Object proc, register int channel)
/* Sending data to subprocess */
-static sys_jmp_buf send_process_frame;
-static Lisp_Object process_sent_to;
-
-static _Noreturn void
-handle_pipe_signal (int sig)
-{
- sigset_t unblocked;
- sigemptyset (&unblocked);
- sigaddset (&unblocked, SIGPIPE);
- pthread_sigmask (SIG_UNBLOCK, &unblocked, 0);
- sys_longjmp (send_process_frame, 1);
-}
-
-static void
-deliver_pipe_signal (int sig)
-{
- handle_on_main_thread (sig, handle_pipe_signal);
-}
-
/* In send_process, when a write fails temporarily,
wait_reading_process_output is called. It may execute user code,
e.g. timers, that attempts to write new data to the same process.
@@ -5495,14 +5468,12 @@ write_queue_pop (struct Lisp_Process *p, Lisp_Object *obj,
This function can evaluate Lisp code and can garbage collect. */
static void
-send_process (volatile Lisp_Object proc, const char *volatile buf,
- volatile ptrdiff_t len, volatile Lisp_Object object)
+send_process (Lisp_Object proc, const char *buf, ptrdiff_t len,
+ Lisp_Object object)
{
- /* Use volatile to protect variables from being clobbered by longjmp. */
struct Lisp_Process *p = XPROCESS (proc);
ssize_t rv;
struct coding_system *coding;
- struct sigaction old_sigpipe_action;
if (p->raw_status_new)
update_status (p);
@@ -5609,145 +5580,126 @@ send_process (volatile Lisp_Object proc, const char *volatile buf,
pty_max_bytes--;
}
- /* 2000-09-21: Emacs 20.7, sparc-sun-solaris-2.6, GCC 2.95.2,
- CFLAGS="-g -O": The value of the parameter `proc' is clobbered
- when returning with longjmp despite being declared volatile. */
- if (!sys_setjmp (send_process_frame))
- {
- p = XPROCESS (proc); /* Repair any setjmp clobbering. */
- process_sent_to = proc;
+ /* If there is already data in the write_queue, put the new data
+ in the back of queue. Otherwise, ignore it. */
+ if (!NILP (p->write_queue))
+ write_queue_push (p, object, buf, len, 0);
- /* If there is already data in the write_queue, put the new data
- in the back of queue. Otherwise, ignore it. */
- if (!NILP (p->write_queue))
- write_queue_push (p, object, buf, len, 0);
+ do /* while !NILP (p->write_queue) */
+ {
+ ptrdiff_t cur_len = -1;
+ const char *cur_buf;
+ Lisp_Object cur_object;
- do /* while !NILP (p->write_queue) */
+ /* If write_queue is empty, ignore it. */
+ if (!write_queue_pop (p, &cur_object, &cur_buf, &cur_len))
{
- ptrdiff_t cur_len = -1;
- const char *cur_buf;
- Lisp_Object cur_object;
+ cur_len = len;
+ cur_buf = buf;
+ cur_object = object;
+ }
- /* If write_queue is empty, ignore it. */
- if (!write_queue_pop (p, &cur_object, &cur_buf, &cur_len))
+ while (cur_len > 0)
+ {
+ /* Send this batch, using one or more write calls. */
+ ptrdiff_t written = 0;
+ int outfd = p->outfd;
+#ifdef DATAGRAM_SOCKETS
+ if (DATAGRAM_CHAN_P (outfd))
{
- cur_len = len;
- cur_buf = buf;
- cur_object = object;
+ rv = sendto (outfd, cur_buf, cur_len,
+ 0, datagram_address[outfd].sa,
+ datagram_address[outfd].len);
+ if (0 <= rv)
+ written = rv;
+ else if (errno == EMSGSIZE)
+ report_file_error ("sending datagram", Fcons (proc, Qnil));
}
-
- while (cur_len > 0)
- {
- /* Send this batch, using one or more write calls. */
- ptrdiff_t written = 0;
- int outfd = p->outfd;
- struct sigaction action;
- emacs_sigaction_init (&action, deliver_pipe_signal);
- sigaction (SIGPIPE, &action, &old_sigpipe_action);
-#ifdef DATAGRAM_SOCKETS
- if (DATAGRAM_CHAN_P (outfd))
- {
- rv = sendto (outfd, cur_buf, cur_len,
- 0, datagram_address[outfd].sa,
- datagram_address[outfd].len);
- if (0 <= rv)
- written = rv;
- else if (errno == EMSGSIZE)
- {
- sigaction (SIGPIPE, &old_sigpipe_action, 0);
- report_file_error ("sending datagram",
- Fcons (proc, Qnil));
- }
- }
- else
+ else
#endif
- {
+ {
#ifdef HAVE_GNUTLS
- if (p->gnutls_p)
- written = emacs_gnutls_write (p, cur_buf, cur_len);
- else
+ if (p->gnutls_p)
+ written = emacs_gnutls_write (p, cur_buf, cur_len);
+ else
#endif
- written = emacs_write (outfd, cur_buf, cur_len);
- rv = (written ? 0 : -1);
+ written = emacs_write (outfd, cur_buf, cur_len);
+ rv = (written ? 0 : -1);
#ifdef ADAPTIVE_READ_BUFFERING
- if (p->read_output_delay > 0
- && p->adaptive_read_buffering == 1)
- {
- p->read_output_delay = 0;
- process_output_delay_count--;
- p->read_output_skip = 0;
- }
-#endif
+ if (p->read_output_delay > 0
+ && p->adaptive_read_buffering == 1)
+ {
+ p->read_output_delay = 0;
+ process_output_delay_count--;
+ p->read_output_skip = 0;
}
- sigaction (SIGPIPE, &old_sigpipe_action, 0);
+#endif
+ }
- if (rv < 0)
- {
- if (0
+ if (rv < 0)
+ {
+ if (0
#ifdef EWOULDBLOCK
- || errno == EWOULDBLOCK
+ || errno == EWOULDBLOCK
#endif
#ifdef EAGAIN
- || errno == EAGAIN
+ || errno == EAGAIN
#endif
- )
- /* Buffer is full. Wait, accepting input;
- that may allow the program
- to finish doing output and read more. */
- {
+ )
+ /* Buffer is full. Wait, accepting input;
+ that may allow the program
+ to finish doing output and read more. */
+ {
#ifdef BROKEN_PTY_READ_AFTER_EAGAIN
- /* A gross hack to work around a bug in FreeBSD.
- In the following sequence, read(2) returns
- bogus data:
-
- write(2) 1022 bytes
- write(2) 954 bytes, get EAGAIN
- read(2) 1024 bytes in process_read_output
- read(2) 11 bytes in process_read_output
-
- That is, read(2) returns more bytes than have
- ever been written successfully. The 1033 bytes
- read are the 1022 bytes written successfully
- after processing (for example with CRs added if
- the terminal is set up that way which it is
- here). The same bytes will be seen again in a
- later read(2), without the CRs. */
-
- if (errno == EAGAIN)
- {
- int flags = FWRITE;
- ioctl (p->outfd, TIOCFLUSH, &flags);
- }
+ /* A gross hack to work around a bug in FreeBSD.
+ In the following sequence, read(2) returns
+ bogus data:
+
+ write(2) 1022 bytes
+ write(2) 954 bytes, get EAGAIN
+ read(2) 1024 bytes in process_read_output
+ read(2) 11 bytes in process_read_output
+
+ That is, read(2) returns more bytes than have
+ ever been written successfully. The 1033 bytes
+ read are the 1022 bytes written successfully
+ after processing (for example with CRs added if
+ the terminal is set up that way which it is
+ here). The same bytes will be seen again in a
+ later read(2), without the CRs. */
+
+ if (errno == EAGAIN)
+ {
+ int flags = FWRITE;
+ ioctl (p->outfd, TIOCFLUSH, &flags);
+ }
#endif /* BROKEN_PTY_READ_AFTER_EAGAIN */
- /* Put what we should have written in wait_queue. */
- write_queue_push (p, cur_object, cur_buf, cur_len, 1);
- wait_reading_process_output (0, 20 * 1000 * 1000,
- 0, 0, Qnil, NULL, 0);
- /* Reread queue, to see what is left. */
- break;
- }
- else
- /* This is a real error. */
- report_file_error ("writing to process", Fcons (proc, Qnil));
+ /* Put what we should have written in wait_queue. */
+ write_queue_push (p, cur_object, cur_buf, cur_len, 1);
+ wait_reading_process_output (0, 20 * 1000 * 1000,
+ 0, 0, Qnil, NULL, 0);
+ /* Reread queue, to see what is left. */
+ break;
}
- cur_buf += written;
- cur_len -= written;
+ else if (errno == EPIPE)
+ {
+ p->raw_status_new = 0;
+ pset_status (p, list2 (Qexit, make_number (256)));
+ p->tick = ++process_tick;
+ deactivate_process (proc);
+ error ("process %s no longer connected to pipe; closed it",
+ SDATA (p->name));
+ }
+ else
+ /* This is a real error. */
+ report_file_error ("writing to process", Fcons (proc, Qnil));
}
+ cur_buf += written;
+ cur_len -= written;
}
- while (!NILP (p->write_queue));
- }
- else
- {
- sigaction (SIGPIPE, &old_sigpipe_action, 0);
- proc = process_sent_to;
- p = XPROCESS (proc);
- p->raw_status_new = 0;
- pset_status (p, Fcons (Qexit, Fcons (make_number (256), Qnil)));
- p->tick = ++process_tick;
- deactivate_process (proc);
- error ("SIGPIPE raised on process %s; closed it", SDATA (p->name));
}
+ while (!NILP (p->write_queue));
}
DEFUN ("process-send-region", Fprocess_send_region, Sprocess_send_region,
@@ -6178,39 +6130,27 @@ SIGCODE may be an integer, or a symbol whose name is a signal name. */)
#ifdef SIGUSR2
parse_signal ("usr2", SIGUSR2);
#endif
-#ifdef SIGTERM
parse_signal ("term", SIGTERM);
-#endif
#ifdef SIGHUP
parse_signal ("hup", SIGHUP);
#endif
-#ifdef SIGINT
parse_signal ("int", SIGINT);
-#endif
#ifdef SIGQUIT
parse_signal ("quit", SIGQUIT);
#endif
-#ifdef SIGILL
parse_signal ("ill", SIGILL);
-#endif
-#ifdef SIGABRT
parse_signal ("abrt", SIGABRT);
-#endif
#ifdef SIGEMT
parse_signal ("emt", SIGEMT);
#endif
#ifdef SIGKILL
parse_signal ("kill", SIGKILL);
#endif
-#ifdef SIGFPE
parse_signal ("fpe", SIGFPE);
-#endif
#ifdef SIGBUS
parse_signal ("bus", SIGBUS);
#endif
-#ifdef SIGSEGV
parse_signal ("segv", SIGSEGV);
-#endif
#ifdef SIGSYS
parse_signal ("sys", SIGSYS);
#endif
@@ -6375,27 +6315,15 @@ process has been transmitted to the serial port. */)
** Malloc WARNING: This should never call malloc either directly or
indirectly; if it does, that is a bug */
-#ifdef SIGCHLD
-
-/* Record one child's changed status. Return true if a child was found. */
-static bool
-record_child_status_change (void)
+/* Record the changed status of the child process PID with wait status W. */
+void
+record_child_status_change (pid_t pid, int w)
{
+#ifdef SIGCHLD
Lisp_Object proc;
struct Lisp_Process *p;
- pid_t pid;
- int w;
Lisp_Object tail;
- do
- pid = waitpid (-1, &w, WNOHANG | WUNTRACED);
- while (pid < 0 && errno == EINTR);
-
- /* PID == 0 means no processes found, PID == -1 means a real failure.
- Either way, we have done all our job. */
- if (pid <= 0)
- return false;
-
/* Find the process that signaled us, and record its status. */
/* The process can have been deleted by Fdelete_process. */
@@ -6406,7 +6334,7 @@ record_child_status_change (void)
|| (FLOATP (xpid) && pid == XFLOAT_DATA (xpid)))
{
XSETCAR (tail, Qnil);
- return true;
+ return;
}
}
@@ -6476,10 +6404,11 @@ record_child_status_change (void)
if (input_available_clear_time)
*input_available_clear_time = make_emacs_time (0, 0);
}
-
- return true;
+#endif
}
+#ifdef SIGCHLD
+
/* On some systems, the SIGCHLD handler must return right away. If
any more processes want to signal us, we will get another signal.
Otherwise, loop around to use up all the processes that have
@@ -6495,14 +6424,29 @@ enum { CAN_HANDLE_MULTIPLE_CHILDREN = 1 };
static void
handle_child_signal (int sig)
{
- while (record_child_status_change () && CAN_HANDLE_MULTIPLE_CHILDREN)
- continue;
+ do
+ {
+ pid_t pid;
+ int status;
+
+ do
+ pid = waitpid (-1, &status, WNOHANG | WUNTRACED);
+ while (pid < 0 && errno == EINTR);
+
+ /* PID == 0 means no processes found, PID == -1 means a real failure.
+ Either way, we have done all our job. */
+ if (pid <= 0)
+ break;
+
+ record_child_status_change (pid, status);
+ }
+ while (CAN_HANDLE_MULTIPLE_CHILDREN);
}
static void
deliver_child_signal (int sig)
{
- handle_on_main_thread (sig, handle_child_signal);
+ deliver_process_signal (sig, handle_child_signal);
}
#endif /* SIGCHLD */