From a309cc76c93826c6ac7d51bba88900738910e504 Mon Sep 17 00:00:00 2001 From: Matthias Dahl Date: Tue, 24 Oct 2017 15:56:47 +0200 Subject: [PATCH 2/2] * src/process.c (wait_reading_process_output): Fix wait_proc hang. If called recursively (through timers or process filters by the means of accept-process-output), it is possible that the output of wait_proc has already been read by one of those recursive calls, leaving the original call hanging forever if no further output arrives through that fd and no timeout has been specified. Implement proper checks by taking advantage of the process output read accounting. --- src/process.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/process.c b/src/process.c index 904ca60863..d4e152eb1c 100644 --- a/src/process.c +++ b/src/process.c @@ -5003,6 +5003,8 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, struct timespec got_output_end_time = invalid_timespec (); enum { MINIMUM = -1, TIMEOUT, INFINITY } wait; int got_some_output = -1; + EMACS_UINT initial_wait_proc_num_bytes_read = (wait_proc) ? + wait_proc->infd_num_bytes_read : 0; #if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS bool retry_for_async; #endif @@ -5161,6 +5163,20 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, && requeued_events_pending_p ()) break; + /* Timers could have called `accept-process-output', thus reading the output + of wait_proc while we (in the worst case) wait endlessly for it to become + available later. So we need to check if data has been read and break out + early if that is so since our job has been fulfilled. */ + if (wait_proc + && wait_proc->infd_num_bytes_read != initial_wait_proc_num_bytes_read) + { + /* Computations on unsigned types are well defined and won't overflow, + so this is safe even if our initial value > our current value, in + case of a wrap around. (ISO/IEC 9899:1999 §6.2.5/9) */ + got_some_output = wait_proc->infd_num_bytes_read + - initial_wait_proc_num_bytes_read; + } + /* This is so a breakpoint can be put here. */ if (!timespec_valid_p (timer_delay)) wait_reading_process_output_1 (); @@ -5606,7 +5622,21 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, buffered-ahead character if we have one. */ nread = read_process_output (proc, channel); - if ((!wait_proc || wait_proc == XPROCESS (proc)) + + /* In case a filter was run that called `accept-process-output', it is + possible that the output from wait_proc was already read, leaving us + waiting for it endlessly (if no timeout was specified). Thus, we need + to check if data was already read. */ + if (wait_proc + && wait_proc->infd_num_bytes_read != initial_wait_proc_num_bytes_read) + { + /* Computations on unsigned types are well defined and won't overflow, + so this is safe even if our initial value > our current value, in + case of a wrap around. (ISO/IEC 9899:1999 §6.2.5/9) */ + got_some_output = wait_proc->infd_num_bytes_read + - initial_wait_proc_num_bytes_read; + } + else if ((!wait_proc || wait_proc == XPROCESS (proc)) && got_some_output < nread) got_some_output = nread; if (nread > 0) -- 2.15.0