From 20ef56dbc88f517ebf60d89577fc89870d9fe888 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 5 Sep 2012 14:33:53 -0700 Subject: [PATCH] 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. --- src/ChangeLog | 50 ++++++++++ src/atimer.c | 24 ++--- src/data.c | 17 ++-- src/dispnew.c | 20 ++-- src/emacs.c | 98 +++++++++--------- src/floatfns.c | 35 ------- src/keyboard.c | 54 +++++----- src/lisp.h | 3 - src/process.c | 256 ++++++++++++++++++++++++------------------------ src/sysdep.c | 38 +++++++ src/syssignal.h | 23 +---- 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 + + 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 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 . */ 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 #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 . */ #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 . 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 -- 2.20.1