=== modified file 'src/ChangeLog' --- src/ChangeLog 2014-06-18 19:17:23 +0000 +++ src/ChangeLog 2014-06-18 21:32:40 +0000 @@ -1,3 +1,16 @@ +2014-06-18 Paul Eggert + + Avoid hangs in accept-process-output (Bug#17647, Bug#17805). + This fix is backported from the trunk. + * lisp.h, process.c (wait_reading_process_input): + Return int, not bool. All uses changed. + * process.c (SELECT_CANT_DO_WRITE_MASK): + Remove macro, replacing with ... + (SELECT_CAN_DO_WRITE_MASK): ... new constant, with inverted sense. + All uses changed. + (status_notify): New arg WAIT_PROC. Return int, not void. + All uses changed. + 2014-06-18 Eli Zaretskii * image.c [5 < GIFLIB_MAJOR + (1 <= GIFLIB_MINOR)]: Declare the === modified file 'src/lisp.h' --- src/lisp.h 2014-06-01 07:06:28 +0000 +++ src/lisp.h 2014-06-18 21:26:42 +0000 @@ -4187,10 +4187,8 @@ /* Defined in process.c. */ extern Lisp_Object QCtype, Qlocal; extern void kill_buffer_processes (Lisp_Object); -extern bool wait_reading_process_output (intmax_t, int, int, bool, - Lisp_Object, - struct Lisp_Process *, - int); +extern int wait_reading_process_output (intmax_t, int, int, bool, Lisp_Object, + struct Lisp_Process *, int); /* Max value for the first argument of wait_reading_process_output. */ #if __GNUC__ == 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 5) /* Work around a bug in GCC 3.4.2, known to be fixed in GCC 4.6.3. === modified file 'src/process.c' --- src/process.c 2014-05-03 20:13:10 +0000 +++ src/process.c 2014-06-18 21:27:30 +0000 @@ -224,8 +224,9 @@ /* Only W32 has this, it really means that select can't take write mask. */ #ifdef BROKEN_NON_BLOCKING_CONNECT #undef NON_BLOCKING_CONNECT -#define SELECT_CANT_DO_WRITE_MASK +enum { SELECT_CAN_DO_WRITE_MASK = false }; #else +enum { SELECT_CAN_DO_WRITE_MASK = true }; #ifndef NON_BLOCKING_CONNECT #ifdef HAVE_SELECT #if defined (HAVE_GETPEERNAME) || defined (GNU_LINUX) @@ -281,7 +282,7 @@ static bool keyboard_bit_set (fd_set *); #endif static void deactivate_process (Lisp_Object); -static void status_notify (struct Lisp_Process *); +static int status_notify (struct Lisp_Process *, struct Lisp_Process *); static int read_process_output (Lisp_Object, int); static void handle_child_signal (int); static void create_pty (Lisp_Object); @@ -870,7 +871,7 @@ { pset_status (p, list2 (Qexit, make_number (0))); p->tick = ++process_tick; - status_notify (p); + status_notify (p, NULL); redisplay_preserve_echo_area (13); } else @@ -890,7 +891,7 @@ pset_status (p, list2 (Qsignal, make_number (SIGKILL))); p->tick = ++process_tick; - status_notify (p); + status_notify (p, NULL); redisplay_preserve_echo_area (13); } } @@ -3997,12 +3998,13 @@ nsecs = 0; return - (wait_reading_process_output (secs, nsecs, 0, 0, + ((wait_reading_process_output (secs, nsecs, 0, 0, Qnil, !NILP (process) ? XPROCESS (process) : NULL, NILP (just_this_one) ? 0 : !INTEGERP (just_this_one) ? 1 : -1) - ? Qt : Qnil); + <= 0) + ? Qnil : Qt); } /* Accept a connection for server process SERVER on CHANNEL. */ @@ -4274,11 +4276,10 @@ (suspending output from other processes). A negative value means don't run any timers either. - If WAIT_PROC is specified, then the function returns true if we - received input from that process before the timeout elapsed. - Otherwise, return true if we received input from any process. */ + Return true if we received input from WAIT_PROC, or from any + process if WAIT_PROC is null. */ -bool +int wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, bool do_display, Lisp_Object wait_for_cell, @@ -4293,8 +4294,7 @@ int xerrno; Lisp_Object proc; struct timespec timeout, end_time; - int wait_channel = -1; - bool got_some_input = 0; + int got_some_input = -1; ptrdiff_t count = SPECPDL_INDEX (); FD_ZERO (&Available); @@ -4305,10 +4305,6 @@ && EQ (XCAR (wait_proc->status), Qexit))) message1 ("Blocking call to accept-process-output with quit inhibited!!"); - /* If wait_proc is a process to watch, set wait_channel accordingly. */ - if (wait_proc != NULL) - wait_channel = wait_proc->infd; - record_unwind_protect_int (wait_reading_process_output_unwind, waiting_for_user_input_p); waiting_for_user_input_p = read_kbd; @@ -4345,6 +4341,10 @@ if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell))) break; + /* After reading input, vacuum up any leftovers without waiting. */ + if (0 <= got_some_input) + nsecs = -1; + /* Compute time from now till when time limit is up. */ /* Exit if already run out. */ if (nsecs < 0) @@ -4463,7 +4463,7 @@ /* It's okay for us to do this and then continue with the loop, since timeout has already been zeroed out. */ clear_waiting_for_input (); - status_notify (NULL); + got_some_input = status_notify (NULL, wait_proc); if (do_display) redisplay_preserve_echo_area (13); } } @@ -4485,18 +4485,23 @@ while (wait_proc->infd >= 0) { int nread = read_process_output (proc, wait_proc->infd); - - if (nread == 0) - break; - - if (nread > 0) - got_some_input = read_some_bytes = 1; - else if (nread == -1 && (errno == EIO || errno == EAGAIN)) - break; + if (nread < 0) + { + if (errno == EIO || errno == EAGAIN) + break; #ifdef EWOULDBLOCK - else if (nread == -1 && EWOULDBLOCK == errno) - break; + if (errno == EWOULDBLOCK) + break; #endif + } + else + { + if (got_some_input < nread) + got_some_input = nread; + if (nread == 0) + break; + read_some_bytes = true; + } } if (read_some_bytes && do_display) redisplay_preserve_echo_area (10); @@ -4527,12 +4532,8 @@ else Available = input_wait_mask; Writeok = write_mask; -#ifdef SELECT_CANT_DO_WRITE_MASK - check_write = 0; -#else - check_write = 1; -#endif - check_delay = wait_channel >= 0 ? 0 : process_output_delay_count; + check_delay = wait_proc ? 0 : process_output_delay_count; + check_write = SELECT_CAN_DO_WRITE_MASK; } /* If frame size has changed or the window is newly mapped, @@ -4558,6 +4559,7 @@ { nfds = read_kbd ? 0 : 1; no_avail = 1; + FD_ZERO (&Available); } if (!no_avail) @@ -4567,7 +4569,7 @@ /* Set the timeout for adaptive read buffering if any process has non-zero read_output_skip and non-zero read_output_delay, and we are not reading output for a - specific wait_channel. It is not executed if + specific process. It is not executed if Vprocess_adaptive_read_buffering is nil. */ if (process_output_skip && check_delay > 0) { @@ -4679,12 +4681,6 @@ report_file_errno ("Failed select", Qnil, xerrno); } - if (no_avail) - { - FD_ZERO (&Available); - check_write = 0; - } - /* Check for keyboard input */ /* If there is any, return immediately to give it higher priority than subprocesses */ @@ -4751,9 +4747,6 @@ handle_input_available_signal (SIGIO); #endif - if (! wait_proc) - got_some_input |= nfds > 0; - /* If checking input just got us a size-change event from X, obey it now if we should. */ if (read_kbd || ! NILP (wait_for_cell)) @@ -4785,12 +4778,6 @@ /* If waiting for this channel, arrange to return as soon as no more input to be processed. No more waiting. */ - if (wait_channel == channel) - { - wait_channel = -1; - nsecs = -1; - got_some_input = 1; - } proc = chan_process[channel]; if (NILP (proc)) continue; @@ -4806,6 +4793,8 @@ buffered-ahead character if we have one. */ nread = read_process_output (proc, channel); + if ((!wait_proc || wait_proc == XPROCESS (proc)) && got_some_input < nread) + got_some_input = nread; if (nread > 0) { /* Since read_process_output can run a filter, @@ -5826,7 +5815,7 @@ p->tick = ++process_tick; if (!nomsg) { - status_notify (NULL); + status_notify (NULL, NULL); redisplay_preserve_echo_area (13); } } @@ -6354,14 +6343,20 @@ /* Report all recent events of a change in process status (either run the sentinel or output a message). This is usually done while Emacs is waiting for keyboard input - but can be done at other times. */ - -static void -status_notify (struct Lisp_Process *deleting_process) + but can be done at other times. + + Return positive if any input was received from WAIT_PROC (or from + any process if WAIT_PROC is null), zero if input was attempted but + none received, and negative if we didn't even try. */ + +static int +status_notify (struct Lisp_Process *deleting_process, + struct Lisp_Process *wait_proc) { - register Lisp_Object proc; + Lisp_Object proc; Lisp_Object tail, msg; struct gcpro gcpro1, gcpro2; + int got_some_input = -1; tail = Qnil; msg = Qnil; @@ -6391,8 +6386,14 @@ /* Network or serial process not stopped: */ && ! EQ (p->command, Qt) && p->infd >= 0 - && p != deleting_process - && read_process_output (proc, p->infd) > 0); + && p != deleting_process) + { + int nread = read_process_output (proc, p->infd); + if (got_some_input < nread) + got_some_input = nread; + if (nread <= 0) + break; + } /* Get the text to use for the message. */ if (p->raw_status_new) @@ -6424,6 +6425,7 @@ update_mode_lines = 24; /* In case buffers use %s in mode-line-format. */ UNGCPRO; + return got_some_input; } DEFUN ("internal-default-process-sentinel", Finternal_default_process_sentinel, @@ -6635,9 +6637,11 @@ DO_DISPLAY means redisplay should be done to show subprocess output that arrives. - Return true if we received input from any process. */ + Return positive if we received input from WAIT_PROC (or from any + process if WAIT_PROC is null), zero if we attempted to receive + input but got none, and negative if we didn't even try. */ -bool +int wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, bool do_display, Lisp_Object wait_for_cell, @@ -6825,7 +6829,7 @@ start_polling (); - return 0; + return -1; } #endif /* not subprocesses */