diff options
| author | Paul Eggert | 2012-09-05 14:33:53 -0700 |
|---|---|---|
| committer | Paul Eggert | 2012-09-05 14:33:53 -0700 |
| commit | 20ef56dbc88f517ebf60d89577fc89870d9fe888 (patch) | |
| tree | e93c0bc49e89e86836575b2b4443c699a237621d /src/process.c | |
| parent | a4e6c042f8f66c47209d76d863adbf5ea3f84a96 (diff) | |
| download | emacs-20ef56dbc88f517ebf60d89577fc89870d9fe888.tar.gz emacs-20ef56dbc88f517ebf60d89577fc89870d9fe888.zip | |
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.
Diffstat (limited to 'src/process.c')
| -rw-r--r-- | src/process.c | 256 |
1 files changed, 129 insertions, 127 deletions
diff --git a/src/process.c b/src/process.c index 5677da36881..3a6615fb505 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -124,6 +124,14 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 124 | #include "xgselect.h" | 124 | #include "xgselect.h" |
| 125 | #endif | 125 | #endif |
| 126 | 126 | ||
| 127 | #ifndef WNOHANG | ||
| 128 | # undef waitpid | ||
| 129 | # define waitpid(pid, status, options) wait (status) | ||
| 130 | #endif | ||
| 131 | #ifndef WUNTRACED | ||
| 132 | # define WUNTRACED 0 | ||
| 133 | #endif | ||
| 134 | |||
| 127 | /* Work around GCC 4.7.0 bug with strict overflow checking; see | 135 | /* Work around GCC 4.7.0 bug with strict overflow checking; see |
| 128 | <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52904>. | 136 | <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52904>. |
| 129 | These lines can be removed once the GCC bug is fixed. */ | 137 | These lines can be removed once the GCC bug is fixed. */ |
| @@ -801,7 +809,7 @@ get_process (register Lisp_Object name) | |||
| 801 | #ifdef SIGCHLD | 809 | #ifdef SIGCHLD |
| 802 | /* Fdelete_process promises to immediately forget about the process, but in | 810 | /* Fdelete_process promises to immediately forget about the process, but in |
| 803 | reality, Emacs needs to remember those processes until they have been | 811 | reality, Emacs needs to remember those processes until they have been |
| 804 | treated by sigchld_handler; otherwise this handler would consider the | 812 | treated by the SIGCHLD handler; otherwise this handler would consider the |
| 805 | process as being synchronous and say that the synchronous process is | 813 | process as being synchronous and say that the synchronous process is |
| 806 | dead. */ | 814 | dead. */ |
| 807 | static Lisp_Object deleted_pid_list; | 815 | static Lisp_Object deleted_pid_list; |
| @@ -849,7 +857,8 @@ nil, indicating the current buffer's process. */) | |||
| 849 | #endif | 857 | #endif |
| 850 | { | 858 | { |
| 851 | Fkill_process (process, Qnil); | 859 | Fkill_process (process, Qnil); |
| 852 | /* Do this now, since remove_process will make sigchld_handler do nothing. */ | 860 | /* Do this now, since remove_process will make the |
| 861 | SIGCHLD handler do nothing. */ | ||
| 853 | pset_status (p, Fcons (Qsignal, Fcons (make_number (SIGKILL), Qnil))); | 862 | pset_status (p, Fcons (Qsignal, Fcons (make_number (SIGKILL), Qnil))); |
| 854 | p->tick = ++process_tick; | 863 | p->tick = ++process_tick; |
| 855 | status_notify (p); | 864 | status_notify (p); |
| @@ -1728,7 +1737,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) | |||
| 1728 | if (inchannel > max_process_desc) | 1737 | if (inchannel > max_process_desc) |
| 1729 | max_process_desc = inchannel; | 1738 | max_process_desc = inchannel; |
| 1730 | 1739 | ||
| 1731 | /* Until we store the proper pid, enable sigchld_handler | 1740 | /* Until we store the proper pid, enable the SIGCHLD handler |
| 1732 | to recognize an unknown pid as standing for this process. | 1741 | to recognize an unknown pid as standing for this process. |
| 1733 | It is very important not to let this `marker' value stay | 1742 | It is very important not to let this `marker' value stay |
| 1734 | in the table after this function has returned; if it does | 1743 | 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, | |||
| 4956 | 4965 | ||
| 4957 | if (p->pid == -2) | 4966 | if (p->pid == -2) |
| 4958 | { | 4967 | { |
| 4959 | /* If the EIO occurs on a pty, sigchld_handler's | 4968 | /* If the EIO occurs on a pty, the SIGCHLD handler's |
| 4960 | waitpid() will not find the process object to | 4969 | waitpid call will not find the process object to |
| 4961 | delete. Do it here. */ | 4970 | delete. Do it here. */ |
| 4962 | p->tick = ++process_tick; | 4971 | p->tick = ++process_tick; |
| 4963 | pset_status (p, Qfailed); | 4972 | pset_status (p, Qfailed); |
| @@ -5422,18 +5431,19 @@ read_process_output (Lisp_Object proc, register int channel) | |||
| 5422 | static jmp_buf send_process_frame; | 5431 | static jmp_buf send_process_frame; |
| 5423 | static Lisp_Object process_sent_to; | 5432 | static Lisp_Object process_sent_to; |
| 5424 | 5433 | ||
| 5425 | #ifndef FORWARD_SIGNAL_TO_MAIN_THREAD | 5434 | static _Noreturn void |
| 5426 | static _Noreturn void send_process_trap (int); | 5435 | handle_pipe_signal (int sig) |
| 5427 | #endif | ||
| 5428 | |||
| 5429 | static void | ||
| 5430 | send_process_trap (int ignore) | ||
| 5431 | { | 5436 | { |
| 5432 | SIGNAL_THREAD_CHECK (SIGPIPE); | ||
| 5433 | sigunblock (sigmask (SIGPIPE)); | 5437 | sigunblock (sigmask (SIGPIPE)); |
| 5434 | _longjmp (send_process_frame, 1); | 5438 | _longjmp (send_process_frame, 1); |
| 5435 | } | 5439 | } |
| 5436 | 5440 | ||
| 5441 | static void | ||
| 5442 | deliver_pipe_signal (int sig) | ||
| 5443 | { | ||
| 5444 | handle_on_main_thread (sig, handle_pipe_signal); | ||
| 5445 | } | ||
| 5446 | |||
| 5437 | /* In send_process, when a write fails temporarily, | 5447 | /* In send_process, when a write fails temporarily, |
| 5438 | wait_reading_process_output is called. It may execute user code, | 5448 | wait_reading_process_output is called. It may execute user code, |
| 5439 | e.g. timers, that attempts to write new data to the same process. | 5449 | 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, | |||
| 5663 | /* Send this batch, using one or more write calls. */ | 5673 | /* Send this batch, using one or more write calls. */ |
| 5664 | ptrdiff_t written = 0; | 5674 | ptrdiff_t written = 0; |
| 5665 | int outfd = p->outfd; | 5675 | int outfd = p->outfd; |
| 5666 | old_sigpipe = (void (*) (int)) signal (SIGPIPE, send_process_trap); | 5676 | old_sigpipe = signal (SIGPIPE, deliver_pipe_signal); |
| 5667 | #ifdef DATAGRAM_SOCKETS | 5677 | #ifdef DATAGRAM_SOCKETS |
| 5668 | if (DATAGRAM_CHAN_P (outfd)) | 5678 | if (DATAGRAM_CHAN_P (outfd)) |
| 5669 | { | 5679 | { |
| @@ -6397,143 +6407,135 @@ process has been transmitted to the serial port. */) | |||
| 6397 | indirectly; if it does, that is a bug */ | 6407 | indirectly; if it does, that is a bug */ |
| 6398 | 6408 | ||
| 6399 | #ifdef SIGCHLD | 6409 | #ifdef SIGCHLD |
| 6400 | static void | 6410 | |
| 6401 | sigchld_handler (int signo) | 6411 | /* Record one child's changed status. Return true if a child was found. */ |
| 6412 | static bool | ||
| 6413 | record_child_status_change (void) | ||
| 6402 | { | 6414 | { |
| 6403 | int old_errno = errno; | ||
| 6404 | Lisp_Object proc; | 6415 | Lisp_Object proc; |
| 6405 | struct Lisp_Process *p; | 6416 | struct Lisp_Process *p; |
| 6417 | pid_t pid; | ||
| 6418 | int w; | ||
| 6419 | Lisp_Object tail; | ||
| 6406 | 6420 | ||
| 6407 | SIGNAL_THREAD_CHECK (signo); | 6421 | do |
| 6408 | 6422 | pid = waitpid (-1, &w, WNOHANG | WUNTRACED); | |
| 6409 | while (1) | 6423 | while (pid < 0 && errno == EINTR); |
| 6410 | { | ||
| 6411 | pid_t pid; | ||
| 6412 | int w; | ||
| 6413 | Lisp_Object tail; | ||
| 6414 | |||
| 6415 | #ifdef WNOHANG | ||
| 6416 | #ifndef WUNTRACED | ||
| 6417 | #define WUNTRACED 0 | ||
| 6418 | #endif /* no WUNTRACED */ | ||
| 6419 | /* Keep trying to get a status until we get a definitive result. */ | ||
| 6420 | do | ||
| 6421 | { | ||
| 6422 | errno = 0; | ||
| 6423 | pid = waitpid (-1, &w, WNOHANG | WUNTRACED); | ||
| 6424 | } | ||
| 6425 | while (pid < 0 && errno == EINTR); | ||
| 6426 | |||
| 6427 | if (pid <= 0) | ||
| 6428 | { | ||
| 6429 | /* PID == 0 means no processes found, PID == -1 means a real | ||
| 6430 | failure. We have done all our job, so return. */ | ||
| 6431 | 6424 | ||
| 6432 | errno = old_errno; | 6425 | /* PID == 0 means no processes found, PID == -1 means a real failure. |
| 6433 | return; | 6426 | Either way, we have done all our job. */ |
| 6434 | } | 6427 | if (pid <= 0) |
| 6435 | #else | 6428 | return false; |
| 6436 | pid = wait (&w); | ||
| 6437 | #endif /* no WNOHANG */ | ||
| 6438 | 6429 | ||
| 6439 | /* Find the process that signaled us, and record its status. */ | 6430 | /* Find the process that signaled us, and record its status. */ |
| 6440 | 6431 | ||
| 6441 | /* The process can have been deleted by Fdelete_process. */ | 6432 | /* The process can have been deleted by Fdelete_process. */ |
| 6442 | for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail)) | 6433 | for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail)) |
| 6434 | { | ||
| 6435 | Lisp_Object xpid = XCAR (tail); | ||
| 6436 | if ((INTEGERP (xpid) && pid == XINT (xpid)) | ||
| 6437 | || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid))) | ||
| 6443 | { | 6438 | { |
| 6444 | Lisp_Object xpid = XCAR (tail); | 6439 | XSETCAR (tail, Qnil); |
| 6445 | if ((INTEGERP (xpid) && pid == XINT (xpid)) | 6440 | return true; |
| 6446 | || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid))) | ||
| 6447 | { | ||
| 6448 | XSETCAR (tail, Qnil); | ||
| 6449 | goto sigchld_end_of_loop; | ||
| 6450 | } | ||
| 6451 | } | 6441 | } |
| 6442 | } | ||
| 6452 | 6443 | ||
| 6453 | /* Otherwise, if it is asynchronous, it is in Vprocess_alist. */ | 6444 | /* Otherwise, if it is asynchronous, it is in Vprocess_alist. */ |
| 6445 | p = 0; | ||
| 6446 | for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) | ||
| 6447 | { | ||
| 6448 | proc = XCDR (XCAR (tail)); | ||
| 6449 | p = XPROCESS (proc); | ||
| 6450 | if (EQ (p->type, Qreal) && p->pid == pid) | ||
| 6451 | break; | ||
| 6454 | p = 0; | 6452 | p = 0; |
| 6455 | for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) | 6453 | } |
| 6456 | { | ||
| 6457 | proc = XCDR (XCAR (tail)); | ||
| 6458 | p = XPROCESS (proc); | ||
| 6459 | if (EQ (p->type, Qreal) && p->pid == pid) | ||
| 6460 | break; | ||
| 6461 | p = 0; | ||
| 6462 | } | ||
| 6463 | |||
| 6464 | /* Look for an asynchronous process whose pid hasn't been filled | ||
| 6465 | in yet. */ | ||
| 6466 | if (p == 0) | ||
| 6467 | for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) | ||
| 6468 | { | ||
| 6469 | proc = XCDR (XCAR (tail)); | ||
| 6470 | p = XPROCESS (proc); | ||
| 6471 | if (p->pid == -1) | ||
| 6472 | break; | ||
| 6473 | p = 0; | ||
| 6474 | } | ||
| 6475 | |||
| 6476 | /* Change the status of the process that was found. */ | ||
| 6477 | if (p != 0) | ||
| 6478 | { | ||
| 6479 | int clear_desc_flag = 0; | ||
| 6480 | 6454 | ||
| 6481 | p->tick = ++process_tick; | 6455 | /* Look for an asynchronous process whose pid hasn't been filled |
| 6482 | p->raw_status = w; | 6456 | in yet. */ |
| 6483 | p->raw_status_new = 1; | 6457 | if (! p) |
| 6458 | for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) | ||
| 6459 | { | ||
| 6460 | proc = XCDR (XCAR (tail)); | ||
| 6461 | p = XPROCESS (proc); | ||
| 6462 | if (p->pid == -1) | ||
| 6463 | break; | ||
| 6464 | p = 0; | ||
| 6465 | } | ||
| 6484 | 6466 | ||
| 6485 | /* If process has terminated, stop waiting for its output. */ | 6467 | /* Change the status of the process that was found. */ |
| 6486 | if ((WIFSIGNALED (w) || WIFEXITED (w)) | 6468 | if (p) |
| 6487 | && p->infd >= 0) | 6469 | { |
| 6488 | clear_desc_flag = 1; | 6470 | int clear_desc_flag = 0; |
| 6489 | 6471 | ||
| 6490 | /* We use clear_desc_flag to avoid a compiler bug in Microsoft C. */ | 6472 | p->tick = ++process_tick; |
| 6491 | if (clear_desc_flag) | 6473 | p->raw_status = w; |
| 6492 | { | 6474 | p->raw_status_new = 1; |
| 6493 | FD_CLR (p->infd, &input_wait_mask); | ||
| 6494 | FD_CLR (p->infd, &non_keyboard_wait_mask); | ||
| 6495 | } | ||
| 6496 | 6475 | ||
| 6497 | /* Tell wait_reading_process_output that it needs to wake up and | 6476 | /* If process has terminated, stop waiting for its output. */ |
| 6498 | look around. */ | 6477 | if ((WIFSIGNALED (w) || WIFEXITED (w)) |
| 6499 | if (input_available_clear_time) | 6478 | && p->infd >= 0) |
| 6500 | *input_available_clear_time = make_emacs_time (0, 0); | 6479 | clear_desc_flag = 1; |
| 6501 | } | ||
| 6502 | 6480 | ||
| 6503 | /* There was no asynchronous process found for that pid: we have | 6481 | /* We use clear_desc_flag to avoid a compiler bug in Microsoft C. */ |
| 6504 | a synchronous process. */ | 6482 | if (clear_desc_flag) |
| 6505 | else | ||
| 6506 | { | 6483 | { |
| 6507 | synch_process_alive = 0; | 6484 | FD_CLR (p->infd, &input_wait_mask); |
| 6508 | 6485 | FD_CLR (p->infd, &non_keyboard_wait_mask); | |
| 6509 | /* Report the status of the synchronous process. */ | ||
| 6510 | if (WIFEXITED (w)) | ||
| 6511 | synch_process_retcode = WEXITSTATUS (w); | ||
| 6512 | else if (WIFSIGNALED (w)) | ||
| 6513 | synch_process_termsig = WTERMSIG (w); | ||
| 6514 | |||
| 6515 | /* Tell wait_reading_process_output that it needs to wake up and | ||
| 6516 | look around. */ | ||
| 6517 | if (input_available_clear_time) | ||
| 6518 | *input_available_clear_time = make_emacs_time (0, 0); | ||
| 6519 | } | 6486 | } |
| 6520 | 6487 | ||
| 6521 | sigchld_end_of_loop: | 6488 | /* Tell wait_reading_process_output that it needs to wake up and |
| 6522 | ; | 6489 | look around. */ |
| 6490 | if (input_available_clear_time) | ||
| 6491 | *input_available_clear_time = make_emacs_time (0, 0); | ||
| 6492 | } | ||
| 6493 | /* There was no asynchronous process found for that pid: we have | ||
| 6494 | a synchronous process. */ | ||
| 6495 | else | ||
| 6496 | { | ||
| 6497 | synch_process_alive = 0; | ||
| 6498 | |||
| 6499 | /* Report the status of the synchronous process. */ | ||
| 6500 | if (WIFEXITED (w)) | ||
| 6501 | synch_process_retcode = WEXITSTATUS (w); | ||
| 6502 | else if (WIFSIGNALED (w)) | ||
| 6503 | synch_process_termsig = WTERMSIG (w); | ||
| 6504 | |||
| 6505 | /* Tell wait_reading_process_output that it needs to wake up and | ||
| 6506 | look around. */ | ||
| 6507 | if (input_available_clear_time) | ||
| 6508 | *input_available_clear_time = make_emacs_time (0, 0); | ||
| 6509 | } | ||
| 6510 | |||
| 6511 | return true; | ||
| 6512 | } | ||
| 6523 | 6513 | ||
| 6524 | /* On some systems, we must return right away. | 6514 | /* On some systems, the SIGCHLD handler must return right away. If |
| 6525 | If any more processes want to signal us, we will | 6515 | any more processes want to signal us, we will get another signal. |
| 6526 | get another signal. | 6516 | Otherwise, loop around to use up all the processes that have |
| 6527 | Otherwise (on systems that have WNOHANG), loop around | 6517 | something to tell us. */ |
| 6528 | to use up all the processes that have something to tell us. */ | ||
| 6529 | #if (defined WINDOWSNT \ | 6518 | #if (defined WINDOWSNT \ |
| 6530 | || (defined USG && !defined GNU_LINUX \ | 6519 | || (defined USG && !defined GNU_LINUX \ |
| 6531 | && !(defined HPUX && defined WNOHANG))) | 6520 | && !(defined HPUX && defined WNOHANG))) |
| 6532 | errno = old_errno; | 6521 | enum { CAN_HANDLE_MULTIPLE_CHILDREN = 1 }; |
| 6533 | return; | 6522 | #else |
| 6534 | #endif /* USG, but not HPUX with WNOHANG */ | 6523 | enum { CAN_HANDLE_MULTIPLE_CHILDREN = 0 }; |
| 6535 | } | 6524 | #endif |
| 6525 | |||
| 6526 | static void | ||
| 6527 | handle_child_signal (int sig) | ||
| 6528 | { | ||
| 6529 | while (record_child_status_change () && CAN_HANDLE_MULTIPLE_CHILDREN) | ||
| 6530 | continue; | ||
| 6536 | } | 6531 | } |
| 6532 | |||
| 6533 | static void | ||
| 6534 | deliver_child_signal (int sig) | ||
| 6535 | { | ||
| 6536 | handle_on_main_thread (sig, handle_child_signal); | ||
| 6537 | } | ||
| 6538 | |||
| 6537 | #endif /* SIGCHLD */ | 6539 | #endif /* SIGCHLD */ |
| 6538 | 6540 | ||
| 6539 | 6541 | ||
| @@ -7387,7 +7389,7 @@ init_process_emacs (void) | |||
| 7387 | #ifndef CANNOT_DUMP | 7389 | #ifndef CANNOT_DUMP |
| 7388 | if (! noninteractive || initialized) | 7390 | if (! noninteractive || initialized) |
| 7389 | #endif | 7391 | #endif |
| 7390 | signal (SIGCHLD, sigchld_handler); | 7392 | signal (SIGCHLD, deliver_child_signal); |
| 7391 | #endif | 7393 | #endif |
| 7392 | 7394 | ||
| 7393 | FD_ZERO (&input_wait_mask); | 7395 | FD_ZERO (&input_wait_mask); |