1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2025-01-26 19:18:50 +00:00

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.
This commit is contained in:
Paul Eggert 2012-09-05 14:33:53 -07:00
parent a4e6c042f8
commit 20ef56dbc8
11 changed files with 328 additions and 298 deletions

View File

@ -1,3 +1,53 @@
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.

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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. */

View File

@ -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 */
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
do
pid = waitpid (-1, &w, WNOHANG | WUNTRACED);
while (pid < 0 && errno == EINTR);
while (1)
/* 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. */
for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail))
{
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
Lisp_Object xpid = XCAR (tail);
if ((INTEGERP (xpid) && pid == XINT (xpid))
|| (FLOATP (xpid) && pid == XFLOAT_DATA (xpid)))
{
errno = 0;
pid = waitpid (-1, &w, WNOHANG | WUNTRACED);
XSETCAR (tail, Qnil);
return true;
}
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. */
errno = old_errno;
return;
}
#else
pid = wait (&w);
#endif /* no WNOHANG */
/* 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))
{
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;
}
}
/* 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))
}
/* 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;
}
/* Change the status of the process that was found. */
if (p)
{
int clear_desc_flag = 0;
p->tick = ++process_tick;
p->raw_status = w;
p->raw_status_new = 1;
/* If process has terminated, stop waiting for its output. */
if ((WIFSIGNALED (w) || WIFEXITED (w))
&& p->infd >= 0)
clear_desc_flag = 1;
/* We use clear_desc_flag to avoid a compiler bug in Microsoft C. */
if (clear_desc_flag)
{
proc = XCDR (XCAR (tail));
p = XPROCESS (proc);
if (EQ (p->type, Qreal) && p->pid == pid)
break;
p = 0;
FD_CLR (p->infd, &input_wait_mask);
FD_CLR (p->infd, &non_keyboard_wait_mask);
}
/* 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;
}
/* 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;
/* Change the status of the process that was found. */
if (p != 0)
{
int clear_desc_flag = 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);
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;
return true;
}
/* 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);
}
/* 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);
}
sigchld_end_of_loop:
;
/* 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);

View File

@ -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)
{

View File

@ -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