+2012-09-05 Paul Eggert <eggert@cs.ucla.edu>
+
+ 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 <dmantipov@yandex.ru>
Remove redundant or unused things here and there.
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
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.
/* 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;
#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)
{
if (on)
{
- signal (SIGALRM, alarm_signal_handler);
+ signal (SIGALRM, deliver_alarm_signal);
set_alarm ();
}
else
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);
}
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)
{
if (!initialized)
return;
#endif /* CANNOT_DUMP */
- signal (SIGFPE, arith_error);
+ signal (SIGFPE, deliver_arith_signal);
}
#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.
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 */
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;
#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. */
/* 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;
/* 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
#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
\f
/* Code for dealing with Lisp access to the Unix command line. */
# 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. */
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));
}
/* 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
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 */
}
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
# 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. */
return make_float (d);
}
\f
-#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)
void
init_floatfns (void)
{
-#ifdef FLOAT_CATCH_SIGILL
- signal (SIGILL, float_error);
-#endif
in_float = 0;
}
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);
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);
unhold_keyboard_input ();
#ifdef SIGIO
if (!noninteractive)
- signal (SIGIO, input_available_signal);
+ signal (SIGIO, deliver_input_available_signal);
#endif /* SIGIO */
start_polling ();
}
/* 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;
#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 */
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));
}
break;
}
+}
- errno = old_errno;
+static void
+deliver_user_signal (int sig)
+{
+ handle_on_main_thread (sig, handle_user_signal);
}
static char *
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
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. */
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
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
#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. */
#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;
#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);
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
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);
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.
/* 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))
{
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 */
\f
#ifndef CANNOT_DUMP
if (! noninteractive || initialized)
#endif
- signal (SIGCHLD, sigchld_handler);
+ signal (SIGCHLD, deliver_child_signal);
#endif
FD_ZERO (&input_wait_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;
+}
\f
#if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST
static char *my_sys_siglist[NSIG];
{
sigemptyset (&empty_mask);
+#ifdef FORWARD_SIGNAL_TO_MAIN_THREAD
+ main_thread = pthread_self ();
+#endif
+
#if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST
if (! initialized)
{
#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