aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ChangeLog50
-rw-r--r--src/atimer.c24
-rw-r--r--src/data.c17
-rw-r--r--src/dispnew.c20
-rw-r--r--src/emacs.c98
-rw-r--r--src/floatfns.c35
-rw-r--r--src/keyboard.c54
-rw-r--r--src/lisp.h3
-rw-r--r--src/process.c256
-rw-r--r--src/sysdep.c38
-rw-r--r--src/syssignal.h23
11 files changed, 324 insertions, 294 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index 96e3fdd5b4..24d290e163 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,53 @@
+2012-09-05 Paul Eggert <[email protected]>
+
+ Fix race conditions with signal handlers and errno.
+ Be more systematic about preserving errno whenever a signal
+ handler returns, even if it's not in the main thread. Do this by
+ renaming signal handlers to distinguish between signal delivery
+ and signal handling. All uses changed.
+ * atimer.c (deliver_alarm_signal): Rename from alarm_signal_handler.
+ * data.c (deliver_arith_signal): Rename from arith_error.
+ * dispnew.c (deliver_window_change_signal): Rename from
+ window_change_signal.
+ * emacs.c (deliver_error_signal): Rename from fatal_error_signal.
+ (deliver_danger_signal) [SIGDANGER]: Rename from memory_warning_signal.
+ * keyboard.c (deliver_input_available_signal): Rename from
+ input_available_signal.
+ (deliver_user_signal): Rename from handle_user_signal.
+ (deliver_interrupt_signal): Rename from interrupt_signal.
+ * process.c (deliver_pipe_signal): Rename from send_process_trap.
+ (deliver_child_signal): Rename from sigchld_handler.
+ * atimer.c (handle_alarm_signal):
+ * data.c (handle_arith_signal):
+ * dispnew.c (handle_window_change_signal):
+ * emacs.c (handle_fatal_signal, handle_danger_signal):
+ * keyboard.c (handle_input_available_signal):
+ * keyboard.c (handle_user_signal, handle_interrupt_signal):
+ * process.c (handle_pipe_signal, handle_child_signal):
+ New functions, with the actual signal-handling code taken from the
+ original respective signal handlers, sans the sporadic attempts to
+ preserve errno, since that's now done by handle_on_main_thread.
+ * atimer.c (alarm_signal_handler): Remove unnecessary decl.
+ * emacs.c, floatfns.c, lisp.h: Remove unused FLOAT_CATCH_SIGKILL cruft.
+ * emacs.c (main_thread) [FORWARD_SIGNAL_TO_MAIN_THREAD]:
+ Move to sysdep.c.
+ (main) [FORWARD_SIGNAL_TO_MAIN_THREAD]:
+ Move initialization of main_thread to sysdep.c's init_signals.
+ * process.c (waitpid) [!WNOHANG]: #define to wait; that's good enough for
+ our usage, and simplifies the mainline code.
+ (record_child_status_change): New static function, as a helper
+ for handle_child_signal, and with most of the old child handler's
+ contents.
+ (CAN_HANDLE_MULTIPLE_CHILDREN): New constant.
+ (handle_child_signal): Use the above.
+ * sysdep.c (main_thread) [FORWARD_SIGNAL_TO_MAIN_THREAD]:
+ Moved here from emacs.c.
+ (init_signals) [FORWARD_SIGNAL_TO_MAIN_THREAD]: Initialize it;
+ code moved here from emacs.c's main function.
+ * sysdep.c, syssignal.h (handle_on_main_thread): New function,
+ replacing the old SIGNAL_THREAD_CHECK. All uses changed. This
+ lets callers save and restore errno properly.
+
2012-09-05 Dmitry Antipov <[email protected]>
Remove redundant or unused things here and there.
diff --git a/src/atimer.c b/src/atimer.c
index eb3136ae55..060dead9b1 100644
--- a/src/atimer.c
+++ b/src/atimer.c
@@ -41,7 +41,7 @@ static struct atimer *stopped_atimers;
static struct atimer *atimers;
-/* Non-zero means alarm_signal_handler has found ripe timers but
+/* Non-zero means alarm signal handler has found ripe timers but
interrupt_input_blocked was non-zero. In this case, timer
functions are not called until the next UNBLOCK_INPUT because timer
functions are expected to call X, and X cannot be assumed to be
@@ -60,8 +60,6 @@ static void set_alarm (void);
static void schedule_atimer (struct atimer *);
static struct atimer *append_atimer_lists (struct atimer *,
struct atimer *);
-static void alarm_signal_handler (int signo);
-
/* Start a new atimer of type TYPE. TIME specifies when the timer is
ripe. FN is the function to call when the timer fires.
@@ -374,13 +372,9 @@ run_timers (void)
/* Signal handler for SIGALRM. SIGNO is the signal number, i.e.
SIGALRM. */
-void
-alarm_signal_handler (int signo)
+static void
+handle_alarm_signal (int sig)
{
-#ifndef SYNC_INPUT
- SIGNAL_THREAD_CHECK (signo);
-#endif
-
pending_atimers = 1;
#ifdef SYNC_INPUT
pending_signals = 1;
@@ -389,8 +383,14 @@ alarm_signal_handler (int signo)
#endif
}
+static void
+deliver_alarm_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_alarm_signal);
+}
+
-/* Call alarm_signal_handler for pending timers. */
+/* Call alarm signal handler for pending timers. */
void
do_pending_atimers (void)
@@ -412,7 +412,7 @@ turn_on_atimers (bool on)
{
if (on)
{
- signal (SIGALRM, alarm_signal_handler);
+ signal (SIGALRM, deliver_alarm_signal);
set_alarm ();
}
else
@@ -426,5 +426,5 @@ init_atimer (void)
free_atimers = stopped_atimers = atimers = NULL;
pending_atimers = 0;
/* pending_signals is initialized in init_keyboard.*/
- signal (SIGALRM, alarm_signal_handler);
+ signal (SIGALRM, deliver_alarm_signal);
}
diff --git a/src/data.c b/src/data.c
index 415a896235..6151d815b2 100644
--- a/src/data.c
+++ b/src/data.c
@@ -3207,18 +3207,19 @@ syms_of_data (void)
XSYMBOL (intern_c_string ("most-negative-fixnum"))->constant = 1;
}
-#ifndef FORWARD_SIGNAL_TO_MAIN_THREAD
-_Noreturn
-#endif
-static void
-arith_error (int signo)
+static _Noreturn void
+handle_arith_signal (int sig)
{
sigsetmask (SIGEMPTYMASK);
-
- SIGNAL_THREAD_CHECK (signo);
xsignal0 (Qarith_error);
}
+static void
+deliver_arith_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_arith_signal);
+}
+
void
init_data (void)
{
@@ -3230,5 +3231,5 @@ init_data (void)
if (!initialized)
return;
#endif /* CANNOT_DUMP */
- signal (SIGFPE, arith_error);
+ signal (SIGFPE, deliver_arith_signal);
}
diff --git a/src/dispnew.c b/src/dispnew.c
index 17e6c85ac7..e02b33000d 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -5552,17 +5552,15 @@ marginal_area_string (struct window *w, enum window_part part,
#ifdef SIGWINCH
+static void deliver_window_change_signal (int);
+
static void
-window_change_signal (int signalnum) /* If we don't have an argument, */
- /* some compilers complain in signal calls. */
+handle_window_change_signal (int sig)
{
int width, height;
- int old_errno = errno;
-
struct tty_display_info *tty;
- signal (SIGWINCH, window_change_signal);
- SIGNAL_THREAD_CHECK (signalnum);
+ signal (SIGWINCH, deliver_window_change_signal);
/* The frame size change obviously applies to a single
termcap-controlled terminal, but we can't decide which.
@@ -5591,8 +5589,12 @@ window_change_signal (int signalnum) /* If we don't have an argument, */
change_frame_size (XFRAME (frame), height, width, 0, 1, 0);
}
}
+}
- errno = old_errno;
+static void
+deliver_window_change_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_window_change_signal);
}
#endif /* SIGWINCH */
@@ -5604,7 +5606,7 @@ window_change_signal (int signalnum) /* If we don't have an argument, */
void
do_pending_window_change (bool safe)
{
- /* If window_change_signal should have run before, run it now. */
+ /* If window change signal handler should have run before, run it now. */
if (redisplaying_p && !safe)
return;
@@ -6173,7 +6175,7 @@ init_display (void)
#ifndef CANNOT_DUMP
if (initialized)
#endif /* CANNOT_DUMP */
- signal (SIGWINCH, window_change_signal);
+ signal (SIGWINCH, deliver_window_change_signal);
#endif /* SIGWINCH */
/* If running as a daemon, no need to initialize any frames/terminal. */
diff --git a/src/emacs.c b/src/emacs.c
index 18f249dc89..fc92b30af2 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -275,14 +275,6 @@ static int fatal_error_code;
/* True if handling a fatal error already. */
bool fatal_error_in_progress;
-#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD
-/* When compiled with GTK and running under Gnome,
- multiple threads may be created. Keep track of our main
- thread to make sure signals are delivered to it (see syssignal.h). */
-
-pthread_t main_thread;
-#endif
-
#ifdef HAVE_NS
/* NS autrelease pool, for memory management. */
static void *ns_pool;
@@ -291,16 +283,18 @@ static void *ns_pool;
/* Handle bus errors, invalid instruction, etc. */
-#ifndef FLOAT_CATCH_SIGILL
-static
-#endif
-void
-fatal_error_signal (int sig)
+static void
+handle_fatal_signal (int sig)
{
- SIGNAL_THREAD_CHECK (sig);
fatal_error_backtrace (sig, 10);
}
+static void
+deliver_fatal_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_fatal_signal);
+}
+
/* Report a fatal error due to signal SIG, output a backtrace of at
most BACKTRACE_LIMIT lines, and exit. */
_Noreturn void
@@ -340,17 +334,23 @@ fatal_error_backtrace (int sig, int backtrace_limit)
#ifdef SIGDANGER
/* Handler for SIGDANGER. */
-void
-memory_warning_signal (int sig)
-{
- signal (sig, memory_warning_signal);
- SIGNAL_THREAD_CHECK (sig);
+static void deliver_danger_signal (int);
+static void
+handle_danger_signal (int sig)
+{
+ signal (sig, deliver_danger_signal);
malloc_warning ("Operating system warns that virtual memory is running low.\n");
/* It might be unsafe to call do_auto_save now. */
force_auto_save_soon ();
}
+
+static void
+deliver_danger_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_danger_signal);
+}
#endif
/* Code for dealing with Lisp access to the Unix command line. */
@@ -851,10 +851,6 @@ main (int argc, char **argv)
# endif /* not SYNC_INPUT */
#endif /* not SYSTEM_MALLOC */
-#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD
- main_thread = pthread_self ();
-#endif /* FORWARD_SIGNAL_TO_MAIN_THREAD */
-
#if defined (MSDOS) || defined (WINDOWSNT)
/* We do all file input/output as binary files. When we need to translate
newlines, we do that manually. */
@@ -1120,7 +1116,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
That makes nohup work. */
if (! noninteractive
|| signal (SIGHUP, SIG_IGN) != SIG_IGN)
- signal (SIGHUP, fatal_error_signal);
+ signal (SIGHUP, deliver_fatal_signal);
sigunblock (sigmask (SIGHUP));
}
@@ -1135,9 +1131,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
/* Don't catch these signals in batch mode if dumping.
On some machines, this sets static data that would make
signal fail to work right when the dumped Emacs is run. */
- signal (SIGQUIT, fatal_error_signal);
- signal (SIGILL, fatal_error_signal);
- signal (SIGTRAP, fatal_error_signal);
+ signal (SIGQUIT, deliver_fatal_signal);
+ signal (SIGILL, deliver_fatal_signal);
+ signal (SIGTRAP, deliver_fatal_signal);
#ifdef SIGUSR1
add_user_signal (SIGUSR1, "sigusr1");
#endif
@@ -1145,68 +1141,68 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
add_user_signal (SIGUSR2, "sigusr2");
#endif
#ifdef SIGABRT
- signal (SIGABRT, fatal_error_signal);
+ signal (SIGABRT, deliver_fatal_signal);
#endif
#ifdef SIGHWE
- signal (SIGHWE, fatal_error_signal);
+ signal (SIGHWE, deliver_fatal_signal);
#endif
#ifdef SIGPRE
- signal (SIGPRE, fatal_error_signal);
+ signal (SIGPRE, deliver_fatal_signal);
#endif
#ifdef SIGORE
- signal (SIGORE, fatal_error_signal);
+ signal (SIGORE, deliver_fatal_signal);
#endif
#ifdef SIGUME
- signal (SIGUME, fatal_error_signal);
+ signal (SIGUME, deliver_fatal_signal);
#endif
#ifdef SIGDLK
- signal (SIGDLK, fatal_error_signal);
+ signal (SIGDLK, deliver_fatal_signal);
#endif
#ifdef SIGCPULIM
- signal (SIGCPULIM, fatal_error_signal);
+ signal (SIGCPULIM, deliver_fatal_signal);
#endif
#ifdef SIGIOT
/* This is missing on some systems - OS/2, for example. */
- signal (SIGIOT, fatal_error_signal);
+ signal (SIGIOT, deliver_fatal_signal);
#endif
#ifdef SIGEMT
- signal (SIGEMT, fatal_error_signal);
+ signal (SIGEMT, deliver_fatal_signal);
#endif
- signal (SIGFPE, fatal_error_signal);
+ signal (SIGFPE, deliver_fatal_signal);
#ifdef SIGBUS
- signal (SIGBUS, fatal_error_signal);
+ signal (SIGBUS, deliver_fatal_signal);
#endif
- signal (SIGSEGV, fatal_error_signal);
+ signal (SIGSEGV, deliver_fatal_signal);
#ifdef SIGSYS
- signal (SIGSYS, fatal_error_signal);
+ signal (SIGSYS, deliver_fatal_signal);
#endif
/* May need special treatment on MS-Windows. See
http://lists.gnu.org/archive/html/emacs-devel/2010-09/msg01062.html
Please update the doc of kill-emacs, kill-emacs-hook, and
NEWS if you change this.
*/
- if (noninteractive) signal (SIGINT, fatal_error_signal);
- signal (SIGTERM, fatal_error_signal);
+ if (noninteractive) signal (SIGINT, deliver_fatal_signal);
+ signal (SIGTERM, deliver_fatal_signal);
#ifdef SIGXCPU
- signal (SIGXCPU, fatal_error_signal);
+ signal (SIGXCPU, deliver_fatal_signal);
#endif
#ifdef SIGXFSZ
- signal (SIGXFSZ, fatal_error_signal);
+ signal (SIGXFSZ, deliver_fatal_signal);
#endif /* SIGXFSZ */
#ifdef SIGDANGER
/* This just means available memory is getting low. */
- signal (SIGDANGER, memory_warning_signal);
+ signal (SIGDANGER, deliver_danger_signal);
#endif
#ifdef AIX
/* 20 is SIGCHLD, 21 is SIGTTIN, 22 is SIGTTOU. */
- signal (SIGXCPU, fatal_error_signal);
- signal (SIGIOINT, fatal_error_signal);
- signal (SIGGRANT, fatal_error_signal);
- signal (SIGRETRACT, fatal_error_signal);
- signal (SIGSOUND, fatal_error_signal);
- signal (SIGMSG, fatal_error_signal);
+ signal (SIGXCPU, deliver_fatal_signal);
+ signal (SIGIOINT, deliver_fatal_signal);
+ signal (SIGGRANT, deliver_fatal_signal);
+ signal (SIGRETRACT, deliver_fatal_signal);
+ signal (SIGSOUND, deliver_fatal_signal);
+ signal (SIGMSG, deliver_fatal_signal);
#endif /* AIX */
}
diff --git a/src/floatfns.c b/src/floatfns.c
index 706fe7ae1a..f59cf58228 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -37,9 +37,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
Define FLOAT_CHECK_ERRNO if the float library routines set errno.
This has no effect if HAVE_MATHERR is defined.
- Define FLOAT_CATCH_SIGILL if the float library routines signal SIGILL.
- (What systems actually do this? Please let us know.)
-
Define FLOAT_CHECK_DOMAIN if the float library doesn't handle errors by
either setting errno, or signaling SIGFPE/SIGILL. Otherwise, domain and
range checking will happen before calling the float routines. This has
@@ -99,10 +96,6 @@ extern double logb (double);
# include <errno.h>
#endif
-#ifdef FLOAT_CATCH_SIGILL
-static void float_error ();
-#endif
-
/* True while executing in floating point.
This tells float_error what to do. */
@@ -947,31 +940,6 @@ Rounds the value toward zero. */)
return make_float (d);
}
-#ifdef FLOAT_CATCH_SIGILL
-static void
-float_error (int signo)
-{
- if (! in_float)
- fatal_error_signal (signo);
-
-#ifdef BSD_SYSTEM
- sigsetmask (SIGEMPTYMASK);
-#else
- /* Must reestablish handler each time it is called. */
- signal (SIGILL, float_error);
-#endif /* BSD_SYSTEM */
-
- SIGNAL_THREAD_CHECK (signo);
- in_float = 0;
-
- xsignal1 (Qarith_error, float_error_arg);
-}
-
-/* Another idea was to replace the library function `infnan'
- where SIGILL is signaled. */
-
-#endif /* FLOAT_CATCH_SIGILL */
-
#ifdef HAVE_MATHERR
int
matherr (struct exception *x)
@@ -1006,9 +974,6 @@ matherr (struct exception *x)
void
init_floatfns (void)
{
-#ifdef FLOAT_CATCH_SIGILL
- signal (SIGILL, float_error);
-#endif
in_float = 0;
}
diff --git a/src/keyboard.c b/src/keyboard.c
index fe5372e008..128f928091 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -449,9 +449,8 @@ static void restore_getcjmp (jmp_buf);
static Lisp_Object apply_modifiers (int, Lisp_Object);
static void clear_event (struct input_event *);
static Lisp_Object restore_kboard_configuration (Lisp_Object);
-static void interrupt_signal (int signalnum);
#ifdef SIGIO
-static void input_available_signal (int signo);
+static void deliver_input_available_signal (int signo);
#endif
static void handle_interrupt (void);
static _Noreturn void quit_throw_to_read_char (int);
@@ -459,7 +458,7 @@ static void process_special_events (void);
static void timer_start_idle (void);
static void timer_stop_idle (void);
static void timer_resume_idle (void);
-static void handle_user_signal (int);
+static void deliver_user_signal (int);
static char *find_user_signal_name (int);
static int store_user_signal_events (void);
@@ -3833,7 +3832,7 @@ kbd_buffer_get_event (KBOARD **kbp,
unhold_keyboard_input ();
#ifdef SIGIO
if (!noninteractive)
- signal (SIGIO, input_available_signal);
+ signal (SIGIO, deliver_input_available_signal);
#endif /* SIGIO */
start_polling ();
}
@@ -7236,12 +7235,8 @@ process_pending_signals (void)
/* Note SIGIO has been undef'd if FIONREAD is missing. */
static void
-input_available_signal (int signo)
+handle_input_available_signal (int sig)
{
- /* Must preserve main program's value of errno. */
- int old_errno = errno;
- SIGNAL_THREAD_CHECK (signo);
-
#ifdef SYNC_INPUT
interrupt_input_pending = 1;
pending_signals = 1;
@@ -7253,8 +7248,12 @@ input_available_signal (int signo)
#ifndef SYNC_INPUT
handle_async_input ();
#endif
+}
- errno = old_errno;
+static void
+deliver_input_available_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_input_available_signal);
}
#endif /* SIGIO */
@@ -7310,18 +7309,15 @@ add_user_signal (int sig, const char *name)
p->next = user_signals;
user_signals = p;
- signal (sig, handle_user_signal);
+ signal (sig, deliver_user_signal);
}
static void
handle_user_signal (int sig)
{
- int old_errno = errno;
struct user_signal_info *p;
const char *special_event_name = NULL;
- SIGNAL_THREAD_CHECK (sig);
-
if (SYMBOLP (Vdebug_on_event))
special_event_name = SSDATA (SYMBOL_NAME (Vdebug_on_event));
@@ -7355,8 +7351,12 @@ handle_user_signal (int sig)
}
break;
}
+}
- errno = old_errno;
+static void
+deliver_user_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_user_signal);
}
static char *
@@ -10776,17 +10776,10 @@ clear_waiting_for_input (void)
Otherwise, tell QUIT to kill Emacs. */
static void
-interrupt_signal (int signalnum) /* If we don't have an argument, some */
- /* compilers complain in signal calls. */
+handle_interrupt_signal (int sig)
{
- /* Must preserve main program's value of errno. */
- int old_errno = errno;
- struct terminal *terminal;
-
- SIGNAL_THREAD_CHECK (signalnum);
-
/* See if we have an active terminal on our controlling tty. */
- terminal = get_named_tty ("/dev/tty");
+ struct terminal *terminal = get_named_tty ("/dev/tty");
if (!terminal)
{
/* If there are no frames there, let's pretend that we are a
@@ -10807,10 +10800,15 @@ interrupt_signal (int signalnum) /* If we don't have an argument, some */
handle_interrupt ();
}
+}
- errno = old_errno;
+static void
+deliver_interrupt_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_interrupt_signal);
}
+
/* If Emacs is stuck because `inhibit-quit' is true, then keep track
of the number of times C-g has been requested. If C-g is pressed
enough times, then quit anyway. See bug#6585. */
@@ -11404,17 +11402,17 @@ init_keyboard (void)
SIGINT. There is special code in interrupt_signal to exit
Emacs on SIGINT when there are no termcap frames on the
controlling terminal. */
- signal (SIGINT, interrupt_signal);
+ signal (SIGINT, deliver_interrupt_signal);
#ifndef DOS_NT
/* For systems with SysV TERMIO, C-g is set up for both SIGINT and
SIGQUIT and we can't tell which one it will give us. */
- signal (SIGQUIT, interrupt_signal);
+ signal (SIGQUIT, deliver_interrupt_signal);
#endif /* not DOS_NT */
}
/* Note SIGIO has been undef'd if FIONREAD is missing. */
#ifdef SIGIO
if (!noninteractive)
- signal (SIGIO, input_available_signal);
+ signal (SIGIO, deliver_input_available_signal);
#endif /* SIGIO */
/* Use interrupt input by default, if it works and noninterrupt input
diff --git a/src/lisp.h b/src/lisp.h
index bc622ba521..28e8c1cd1e 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3256,9 +3256,6 @@ extern bool display_arg;
extern Lisp_Object decode_env_path (const char *, const char *);
extern Lisp_Object empty_unibyte_string, empty_multibyte_string;
extern Lisp_Object Qfile_name_handler_alist;
-#ifdef FLOAT_CATCH_SIGILL
-extern void fatal_error_signal (int);
-#endif
extern _Noreturn void fatal_error_backtrace (int, int);
extern Lisp_Object Qkill_emacs;
#if HAVE_SETLOCALE
diff --git a/src/process.c b/src/process.c
index 5677da3688..3a6615fb50 100644
--- a/src/process.c
+++ b/src/process.c
@@ -124,6 +124,14 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "xgselect.h"
#endif
+#ifndef WNOHANG
+# undef waitpid
+# define waitpid(pid, status, options) wait (status)
+#endif
+#ifndef WUNTRACED
+# define WUNTRACED 0
+#endif
+
/* Work around GCC 4.7.0 bug with strict overflow checking; see
<http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52904>.
These lines can be removed once the GCC bug is fixed. */
@@ -801,7 +809,7 @@ get_process (register Lisp_Object name)
#ifdef SIGCHLD
/* Fdelete_process promises to immediately forget about the process, but in
reality, Emacs needs to remember those processes until they have been
- treated by sigchld_handler; otherwise this handler would consider the
+ treated by the SIGCHLD handler; otherwise this handler would consider the
process as being synchronous and say that the synchronous process is
dead. */
static Lisp_Object deleted_pid_list;
@@ -849,7 +857,8 @@ nil, indicating the current buffer's process. */)
#endif
{
Fkill_process (process, Qnil);
- /* Do this now, since remove_process will make sigchld_handler do nothing. */
+ /* Do this now, since remove_process will make the
+ SIGCHLD handler do nothing. */
pset_status (p, Fcons (Qsignal, Fcons (make_number (SIGKILL), Qnil)));
p->tick = ++process_tick;
status_notify (p);
@@ -1728,7 +1737,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
if (inchannel > max_process_desc)
max_process_desc = inchannel;
- /* Until we store the proper pid, enable sigchld_handler
+ /* Until we store the proper pid, enable the SIGCHLD handler
to recognize an unknown pid as standing for this process.
It is very important not to let this `marker' value stay
in the table after this function has returned; if it does
@@ -4956,8 +4965,8 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
if (p->pid == -2)
{
- /* If the EIO occurs on a pty, sigchld_handler's
- waitpid() will not find the process object to
+ /* If the EIO occurs on a pty, the SIGCHLD handler's
+ waitpid call will not find the process object to
delete. Do it here. */
p->tick = ++process_tick;
pset_status (p, Qfailed);
@@ -5422,18 +5431,19 @@ read_process_output (Lisp_Object proc, register int channel)
static jmp_buf send_process_frame;
static Lisp_Object process_sent_to;
-#ifndef FORWARD_SIGNAL_TO_MAIN_THREAD
-static _Noreturn void send_process_trap (int);
-#endif
-
-static void
-send_process_trap (int ignore)
+static _Noreturn void
+handle_pipe_signal (int sig)
{
- SIGNAL_THREAD_CHECK (SIGPIPE);
sigunblock (sigmask (SIGPIPE));
_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.
@@ -5663,7 +5673,7 @@ send_process (volatile Lisp_Object proc, const char *volatile buf,
/* Send this batch, using one or more write calls. */
ptrdiff_t written = 0;
int outfd = p->outfd;
- old_sigpipe = (void (*) (int)) signal (SIGPIPE, send_process_trap);
+ old_sigpipe = signal (SIGPIPE, deliver_pipe_signal);
#ifdef DATAGRAM_SOCKETS
if (DATAGRAM_CHAN_P (outfd))
{
@@ -6397,143 +6407,135 @@ process has been transmitted to the serial port. */)
indirectly; if it does, that is a bug */
#ifdef SIGCHLD
-static void
-sigchld_handler (int signo)
+
+/* Record one child's changed status. Return true if a child was found. */
+static bool
+record_child_status_change (void)
{
- int old_errno = errno;
Lisp_Object proc;
struct Lisp_Process *p;
+ pid_t pid;
+ int w;
+ Lisp_Object tail;
- SIGNAL_THREAD_CHECK (signo);
-
- while (1)
- {
- pid_t pid;
- int w;
- Lisp_Object tail;
-
-#ifdef WNOHANG
-#ifndef WUNTRACED
-#define WUNTRACED 0
-#endif /* no WUNTRACED */
- /* Keep trying to get a status until we get a definitive result. */
- do
- {
- errno = 0;
- pid = waitpid (-1, &w, WNOHANG | WUNTRACED);
- }
- while (pid < 0 && errno == EINTR);
-
- if (pid <= 0)
- {
- /* PID == 0 means no processes found, PID == -1 means a real
- failure. We have done all our job, so return. */
+ do
+ pid = waitpid (-1, &w, WNOHANG | WUNTRACED);
+ while (pid < 0 && errno == EINTR);
- errno = old_errno;
- return;
- }
-#else
- pid = wait (&w);
-#endif /* no WNOHANG */
+ /* 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. */
+ /* Find the process that signaled us, and record its status. */
- /* The process can have been deleted by Fdelete_process. */
- for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail))
+ /* The process can have been deleted by Fdelete_process. */
+ for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail))
+ {
+ Lisp_Object xpid = XCAR (tail);
+ if ((INTEGERP (xpid) && pid == XINT (xpid))
+ || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid)))
{
- Lisp_Object xpid = XCAR (tail);
- if ((INTEGERP (xpid) && pid == XINT (xpid))
- || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid)))
- {
- XSETCAR (tail, Qnil);
- goto sigchld_end_of_loop;
- }
+ XSETCAR (tail, Qnil);
+ return true;
}
+ }
- /* Otherwise, if it is asynchronous, it is in Vprocess_alist. */
+ /* Otherwise, if it is asynchronous, it is in Vprocess_alist. */
+ p = 0;
+ for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
+ {
+ proc = XCDR (XCAR (tail));
+ p = XPROCESS (proc);
+ if (EQ (p->type, Qreal) && p->pid == pid)
+ break;
p = 0;
- for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
- {
- proc = XCDR (XCAR (tail));
- p = XPROCESS (proc);
- if (EQ (p->type, Qreal) && p->pid == pid)
- break;
- p = 0;
- }
-
- /* Look for an asynchronous process whose pid hasn't been filled
- in yet. */
- if (p == 0)
- for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
- {
- proc = XCDR (XCAR (tail));
- p = XPROCESS (proc);
- if (p->pid == -1)
- break;
- p = 0;
- }
-
- /* Change the status of the process that was found. */
- if (p != 0)
- {
- int clear_desc_flag = 0;
+ }
- p->tick = ++process_tick;
- p->raw_status = w;
- p->raw_status_new = 1;
+ /* Look for an asynchronous process whose pid hasn't been filled
+ in yet. */
+ if (! p)
+ for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
+ {
+ proc = XCDR (XCAR (tail));
+ p = XPROCESS (proc);
+ if (p->pid == -1)
+ break;
+ p = 0;
+ }
- /* If process has terminated, stop waiting for its output. */
- if ((WIFSIGNALED (w) || WIFEXITED (w))
- && p->infd >= 0)
- clear_desc_flag = 1;
+ /* Change the status of the process that was found. */
+ if (p)
+ {
+ int clear_desc_flag = 0;
- /* We use clear_desc_flag to avoid a compiler bug in Microsoft C. */
- if (clear_desc_flag)
- {
- FD_CLR (p->infd, &input_wait_mask);
- FD_CLR (p->infd, &non_keyboard_wait_mask);
- }
+ p->tick = ++process_tick;
+ p->raw_status = w;
+ p->raw_status_new = 1;
- /* Tell wait_reading_process_output that it needs to wake up and
- look around. */
- if (input_available_clear_time)
- *input_available_clear_time = make_emacs_time (0, 0);
- }
+ /* If process has terminated, stop waiting for its output. */
+ if ((WIFSIGNALED (w) || WIFEXITED (w))
+ && p->infd >= 0)
+ clear_desc_flag = 1;
- /* There was no asynchronous process found for that pid: we have
- a synchronous process. */
- else
+ /* We use clear_desc_flag to avoid a compiler bug in Microsoft C. */
+ if (clear_desc_flag)
{
- synch_process_alive = 0;
-
- /* Report the status of the synchronous process. */
- if (WIFEXITED (w))
- synch_process_retcode = WEXITSTATUS (w);
- else if (WIFSIGNALED (w))
- synch_process_termsig = WTERMSIG (w);
-
- /* Tell wait_reading_process_output that it needs to wake up and
- look around. */
- if (input_available_clear_time)
- *input_available_clear_time = make_emacs_time (0, 0);
+ FD_CLR (p->infd, &input_wait_mask);
+ FD_CLR (p->infd, &non_keyboard_wait_mask);
}
- sigchld_end_of_loop:
- ;
+ /* Tell wait_reading_process_output that it needs to wake up and
+ look around. */
+ if (input_available_clear_time)
+ *input_available_clear_time = make_emacs_time (0, 0);
+ }
+ /* There was no asynchronous process found for that pid: we have
+ a synchronous process. */
+ else
+ {
+ synch_process_alive = 0;
+
+ /* Report the status of the synchronous process. */
+ if (WIFEXITED (w))
+ synch_process_retcode = WEXITSTATUS (w);
+ else if (WIFSIGNALED (w))
+ synch_process_termsig = WTERMSIG (w);
+
+ /* Tell wait_reading_process_output that it needs to wake up and
+ look around. */
+ if (input_available_clear_time)
+ *input_available_clear_time = make_emacs_time (0, 0);
+ }
+
+ return true;
+}
- /* On some systems, we must return right away.
- If any more processes want to signal us, we will
- get another signal.
- Otherwise (on systems that have WNOHANG), loop around
- to use up all the processes that have something to tell us. */
+/* 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
+ something to tell us. */
#if (defined WINDOWSNT \
|| (defined USG && !defined GNU_LINUX \
&& !(defined HPUX && defined WNOHANG)))
- errno = old_errno;
- return;
-#endif /* USG, but not HPUX with WNOHANG */
- }
+enum { CAN_HANDLE_MULTIPLE_CHILDREN = 1 };
+#else
+enum { CAN_HANDLE_MULTIPLE_CHILDREN = 0 };
+#endif
+
+static void
+handle_child_signal (int sig)
+{
+ while (record_child_status_change () && CAN_HANDLE_MULTIPLE_CHILDREN)
+ continue;
}
+
+static void
+deliver_child_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_child_signal);
+}
+
#endif /* SIGCHLD */
@@ -7387,7 +7389,7 @@ init_process_emacs (void)
#ifndef CANNOT_DUMP
if (! noninteractive || initialized)
#endif
- signal (SIGCHLD, sigchld_handler);
+ signal (SIGCHLD, deliver_child_signal);
#endif
FD_ZERO (&input_wait_mask);
diff --git a/src/sysdep.c b/src/sysdep.c
index 1f4de194c6..42b8baf78e 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -1551,6 +1551,40 @@ sys_sigsetmask (sigset_t new_mask)
return (old_mask);
}
+#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD
+pthread_t main_thread;
+#endif
+
+/* If we are on the main thread, handle the signal SIG with HANDLER.
+ Otherwise, redirect the signal to the main thread, blocking it from
+ this thread. POSIX says any thread can receive a signal that is
+ associated with a process, process group, or asynchronous event.
+ On GNU/Linux that is not true, but for other systems (FreeBSD at
+ least) it is. */
+void
+handle_on_main_thread (int sig, signal_handler_t handler)
+{
+ /* Preserve errno, to avoid race conditions with signal handlers that
+ might change errno. Races can occur even in single-threaded hosts. */
+ int old_errno = errno;
+
+ bool on_main_thread = true;
+#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD
+ if (! pthread_equal (pthread_self (), main_thread))
+ {
+ sigset_t blocked;
+ sigemptyset (&blocked);
+ sigaddset (&blocked, sig);
+ pthread_sigmask (SIG_BLOCK, &blocked, 0);
+ pthread_kill (main_thread, sig);
+ on_main_thread = false;
+ }
+#endif
+ if (on_main_thread)
+ handler (sig);
+
+ errno = old_errno;
+}
#if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST
static char *my_sys_siglist[NSIG];
@@ -1565,6 +1599,10 @@ init_signals (void)
{
sigemptyset (&empty_mask);
+#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD
+ main_thread = pthread_self ();
+#endif
+
#if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST
if (! initialized)
{
diff --git a/src/syssignal.h b/src/syssignal.h
index 71151ed4c6..504aff4308 100644
--- a/src/syssignal.h
+++ b/src/syssignal.h
@@ -133,24 +133,5 @@ char *strsignal (int);
#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD
extern pthread_t main_thread;
-#define SIGNAL_THREAD_CHECK(signo) \
- do { \
- if (!pthread_equal (pthread_self (), main_thread)) \
- { \
- /* POSIX says any thread can receive the signal. On GNU/Linux \
- that is not true, but for other systems (FreeBSD at least) \
- it is. So direct the signal to the correct thread and block \
- it from this thread. */ \
- sigset_t new_mask; \
- \
- sigemptyset (&new_mask); \
- sigaddset (&new_mask, signo); \
- pthread_sigmask (SIG_BLOCK, &new_mask, 0); \
- pthread_kill (main_thread, signo); \
- return; \
- } \
- } while (0)
-
-#else /* not FORWARD_SIGNAL_TO_MAIN_THREAD */
-#define SIGNAL_THREAD_CHECK(signo)
-#endif /* not FORWARD_SIGNAL_TO_MAIN_THREAD */
+void handle_on_main_thread (int, signal_handler_t);
+#endif