From b01ac672be1277833964d2d53f6dd26560c70343 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 1 Feb 2017 15:18:44 -0800 Subject: Revamp quitting and fix infloops This fixes some infinite loops that cannot be quitted out of, e.g., (defun foo () (nth most-positive-fixnum '#1=(1 . #1#))) when byte-compiled and when run under X. See: http://lists.gnu.org/archive/html/emacs-devel/2017-01/msg00577.html This also attempts to keep the performance improvements I recently added, as much as possible under the constraint that the infloops must be caught. In some cases this fixes infloop bugs recently introduced when I removed immediate_quit. * src/alloc.c (Fmake_list): Use rarely_quit, not maybe_quit, for speed in the usual case. * src/bytecode.c (exec_byte_code): * src/editfns.c (Fcompare_buffer_substrings): * src/fns.c (Fnthcdr): * src/syntax.c (scan_words, skip_chars, skip_syntaxes) (Fbackward_prefix_chars): Use rarely_quit so that users can C-g out of long loops. * src/callproc.c (call_process_cleanup, call_process): * src/fileio.c (read_non_regular, Finsert_file_contents): * src/indent.c (compute_motion): * src/syntax.c (scan_words, Fforward_comment): Remove now-unnecessary maybe_quit calls. * src/callproc.c (call_process): * src/doc.c (get_doc_string, Fsnarf_documentation): * src/fileio.c (Fcopy_file, read_non_regular, Finsert_file_contents): * src/lread.c (safe_to_load_version): * src/sysdep.c (system_process_attributes) [GNU_LINUX]: Use emacs_read_quit instead of emacs_read in places where C-g handling is safe. * src/eval.c (maybe_quit): Move comment here from lisp.h. * src/fileio.c (Fcopy_file, e_write): Use emacs_write_quit instead of emacs_write_sig in places where C-g handling is safe. * src/filelock.c (create_lock_file): Use emacs_write, not plain write, as emacs_write no longer has a problem. (read_lock_data): Use emacs_read, not read, as emacs_read no longer has a problem. * src/fns.c (rarely_quit): Move to lisp.h and rename to incr_rarely_quit. All uses changed.. * src/fns.c (Fmemq, Fmemql, Fassq, Frassq, Fplist_put, Fplist_member): * src/indent.c (compute_motion): * src/syntax.c (find_defun_start, back_comment, forw_comment) (Fforward_comment, scan_lists, scan_sexps_forward): Use incr_rarely_quit so that users can C-g out of long loops. * src/fns.c (Fnconc): Move incr_rarely_quit call to within inner loop, so that it catches C-g there too. * src/keyboard.c (tty_read_avail_input): Remove commented-out and now-obsolete code dealing with interrupts. * src/lisp.h (rarely_quit, incr_rarely_quit): New functions, the latter moved here from fns.c and renamed from rarely_quit. (emacs_read_quit, emacs_write_quit): New decls. * src/search.c (find_newline, search_buffer, find_newline1): Add maybe_quit to catch C-g. * src/sysdep.c (get_child_status): Always invoke maybe_quit if interruptible, so that the caller need not bother. (emacs_nointr_read, emacs_read_quit, emacs_write_quit): New functions. (emacs_read): Rewrite in terms of emacs_nointr_read. Do not handle C-g or signals; that is now for emacs_read_quit. (emacs_full_write): Replace PROCESS_SIGNALS two-way arg with INTERRUPTIBLE three-way arg. All uses changed. --- src/sysdep.c | 129 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 45 deletions(-) (limited to 'src/sysdep.c') diff --git a/src/sysdep.c b/src/sysdep.c index e172dc0aed4..4155c205712 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -382,19 +382,23 @@ get_child_status (pid_t child, int *status, int options, bool interruptible) so that another thread running glib won't find them. */ eassert (child > 0); - while ((pid = waitpid (child, status, options)) < 0) + while (true) { + /* Note: the MS-Windows emulation of waitpid calls maybe_quit + internally. */ + if (interruptible) + maybe_quit (); + + pid = waitpid (child, status, options); + if (0 <= pid) + break; + /* Check that CHILD is a child process that has not been reaped, and that STATUS and OPTIONS are valid. Otherwise abort, as continuing after this internal error could cause Emacs to become confused and kill innocent-victim processes. */ if (errno != EINTR) emacs_abort (); - - /* Note: the MS-Windows emulation of waitpid calls maybe_quit - internally. */ - if (interruptible) - maybe_quit (); } /* If successful and status is requested, tell wait_reading_process_output @@ -2503,78 +2507,113 @@ emacs_close (int fd) #define MAX_RW_COUNT (INT_MAX >> 18 << 18) #endif -/* Read from FILEDESC to a buffer BUF with size NBYTE, retrying if interrupted. +/* Read from FD to a buffer BUF with size NBYTE. + If interrupted, either quit or retry the read. + Process any quits and pending signals immediately if INTERRUPTIBLE. Return the number of bytes read, which might be less than NBYTE. - On error, set errno and return -1. */ -ptrdiff_t -emacs_read (int fildes, void *buf, ptrdiff_t nbyte) + On error, set errno to a value other than EINTR, and return -1. */ +static ptrdiff_t +emacs_nointr_read (int fd, void *buf, ptrdiff_t nbyte, bool interruptible) { - ssize_t rtnval; + ssize_t result; /* There is no need to check against MAX_RW_COUNT, since no caller ever passes a size that large to emacs_read. */ + do + { + if (interruptible) + maybe_quit (); + result = read (fd, buf, nbyte); + } + while (result < 0 && errno == EINTR); - while ((rtnval = read (fildes, buf, nbyte)) == -1 - && (errno == EINTR)) - maybe_quit (); - return (rtnval); + return result; } -/* Write to FILEDES from a buffer BUF with size NBYTE, retrying if interrupted - or if a partial write occurs. If interrupted, process pending - signals if PROCESS SIGNALS. Return the number of bytes written, setting - errno if this is less than NBYTE. */ +/* Read from FD to a buffer BUF with size NBYTE. + If interrupted, retry the read. Return the number of bytes read, + which might be less than NBYTE. On error, set errno to a value + other than EINTR, and return -1. */ +ptrdiff_t +emacs_read (int fd, void *buf, ptrdiff_t nbyte) +{ + return emacs_nointr_read (fd, buf, nbyte, false); +} + +/* Like emacs_read, but also process quits and pending signals. */ +ptrdiff_t +emacs_read_quit (int fd, void *buf, ptrdiff_t nbyte) +{ + return emacs_nointr_read (fd, buf, nbyte, true); +} + +/* Write to FILEDES from a buffer BUF with size NBYTE, retrying if + interrupted or if a partial write occurs. Process any quits + immediately if INTERRUPTIBLE is positive, and process any pending + signals immediately if INTERRUPTIBLE is nonzero. Return the number + of bytes written; if this is less than NBYTE, set errno to a value + other than EINTR. */ static ptrdiff_t -emacs_full_write (int fildes, char const *buf, ptrdiff_t nbyte, - bool process_signals) +emacs_full_write (int fd, char const *buf, ptrdiff_t nbyte, + int interruptible) { ptrdiff_t bytes_written = 0; while (nbyte > 0) { - ssize_t n = write (fildes, buf, min (nbyte, MAX_RW_COUNT)); + ssize_t n = write (fd, buf, min (nbyte, MAX_RW_COUNT)); if (n < 0) { - if (errno == EINTR) + if (errno != EINTR) + break; + + if (interruptible) { - /* I originally used maybe_quit but that might cause files to - be truncated if you hit C-g in the middle of it. --Stef */ - if (process_signals && pending_signals) + if (0 < interruptible) + maybe_quit (); + if (pending_signals) process_pending_signals (); - continue; } - else - break; } - - buf += n; - nbyte -= n; - bytes_written += n; + else + { + buf += n; + nbyte -= n; + bytes_written += n; + } } return bytes_written; } -/* Write to FILEDES from a buffer BUF with size NBYTE, retrying if - interrupted or if a partial write occurs. Return the number of - bytes written, setting errno if this is less than NBYTE. */ +/* Write to FD from a buffer BUF with size NBYTE, retrying if + interrupted or if a partial write occurs. Do not process quits or + pending signals. Return the number of bytes written, setting errno + if this is less than NBYTE. */ +ptrdiff_t +emacs_write (int fd, void const *buf, ptrdiff_t nbyte) +{ + return emacs_full_write (fd, buf, nbyte, 0); +} + +/* Like emacs_write, but also process pending signals. */ ptrdiff_t -emacs_write (int fildes, void const *buf, ptrdiff_t nbyte) +emacs_write_sig (int fd, void const *buf, ptrdiff_t nbyte) { - return emacs_full_write (fildes, buf, nbyte, 0); + return emacs_full_write (fd, buf, nbyte, -1); } -/* Like emacs_write, but also process pending signals if interrupted. */ +/* Like emacs_write, but also process quits and pending signals. */ ptrdiff_t -emacs_write_sig (int fildes, void const *buf, ptrdiff_t nbyte) +emacs_write_quit (int fd, void const *buf, ptrdiff_t nbyte) { - return emacs_full_write (fildes, buf, nbyte, 1); + return emacs_full_write (fd, buf, nbyte, 1); } /* Write a diagnostic to standard error that contains MESSAGE and a string derived from errno. Preserve errno. Do not buffer stderr. - Do not process pending signals if interrupted. */ + Do not process quits or pending signals if interrupted. */ void emacs_perror (char const *message) { @@ -3168,7 +3207,7 @@ system_process_attributes (Lisp_Object pid) else { record_unwind_protect_int (close_file_unwind, fd); - nread = emacs_read (fd, procbuf, sizeof procbuf - 1); + nread = emacs_read_quit (fd, procbuf, sizeof procbuf - 1); } if (0 < nread) { @@ -3289,7 +3328,7 @@ system_process_attributes (Lisp_Object pid) /* Leave room even if every byte needs escaping below. */ readsize = (cmdline_size >> 1) - nread; - nread_incr = emacs_read (fd, cmdline + nread, readsize); + nread_incr = emacs_read_quit (fd, cmdline + nread, readsize); nread += max (0, nread_incr); } while (nread_incr == readsize); @@ -3402,7 +3441,7 @@ system_process_attributes (Lisp_Object pid) else { record_unwind_protect_int (close_file_unwind, fd); - nread = emacs_read (fd, &pinfo, sizeof pinfo); + nread = emacs_read_quit (fd, &pinfo, sizeof pinfo); } if (nread == sizeof pinfo) -- cgit v1.2.1 From b4c9f9120d8b0da0593f2fbde69b40374f56451d Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 1 Feb 2017 15:18:44 -0800 Subject: Fix quitting bug when buffers are frozen Problem noted by Eli Zaretskii in: http://lists.gnu.org/archive/html/emacs-devel/2017-01/msg00721.html This patch also fixes some other issues in that report. * src/lisp.h (incr_rarely_quit): Remove. All callers changed to use rarely_quit directly. * src/search.c (freeze_buffer_relocation) (thaw_buffer_relocation): New functions. (looking_at_1, fast_looking_at, search_buffer): Use them to fix bug when quitting when buffers are frozen. * src/sysdep.c (emacs_intr_read): Rename from emacs_nointr_read. All uses changed. --- src/sysdep.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/sysdep.c') diff --git a/src/sysdep.c b/src/sysdep.c index 4155c205712..91b2a5cb943 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -2508,12 +2508,12 @@ emacs_close (int fd) #endif /* Read from FD to a buffer BUF with size NBYTE. - If interrupted, either quit or retry the read. - Process any quits and pending signals immediately if INTERRUPTIBLE. + If interrupted, process any quits and pending signals immediately + if INTERRUPTIBLE, and then retry the read unless quitting. Return the number of bytes read, which might be less than NBYTE. On error, set errno to a value other than EINTR, and return -1. */ static ptrdiff_t -emacs_nointr_read (int fd, void *buf, ptrdiff_t nbyte, bool interruptible) +emacs_intr_read (int fd, void *buf, ptrdiff_t nbyte, bool interruptible) { ssize_t result; @@ -2537,14 +2537,14 @@ emacs_nointr_read (int fd, void *buf, ptrdiff_t nbyte, bool interruptible) ptrdiff_t emacs_read (int fd, void *buf, ptrdiff_t nbyte) { - return emacs_nointr_read (fd, buf, nbyte, false); + return emacs_intr_read (fd, buf, nbyte, false); } /* Like emacs_read, but also process quits and pending signals. */ ptrdiff_t emacs_read_quit (int fd, void *buf, ptrdiff_t nbyte) { - return emacs_nointr_read (fd, buf, nbyte, true); + return emacs_intr_read (fd, buf, nbyte, true); } /* Write to FILEDES from a buffer BUF with size NBYTE, retrying if -- cgit v1.2.1