commit 8c0fcaf66733f0538a3f024f383cb34a3c93d73c (HEAD, refs/remotes/origin/master) Author: Noam Postavsky Date: Thu Jan 12 23:32:44 2017 -0500 Avoid inefficient regex in diff-refine-hunk (Bug#25410) * lisp/vc/diff-mode.el (diff--forward-while-leading-char): New function. (diff-refine-hunk): Use it instead of trying to match multiple lines with a single lines. diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index 9dfcd944bb..b50b4a254f 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -2062,6 +2062,15 @@ For use in `add-log-current-defun-function'." (declare-function smerge-refine-subst "smerge-mode" (beg1 end1 beg2 end2 props-c &optional preproc props-r props-a)) +(defun diff--forward-while-leading-char (char bound) + "Move point until reaching a line not starting with CHAR. +Return new point, if it was moved." + (let ((pt nil)) + (while (and (< (point) bound) (eql (following-char) char)) + (forward-line 1) + (setq pt (point))) + pt)) + (defun diff-refine-hunk () "Highlight changes of hunk at point at a finer granularity." (interactive) @@ -2081,16 +2090,18 @@ For use in `add-log-current-defun-function'." (goto-char beg) (pcase style (`unified - (while (re-search-forward - (eval-when-compile - (let ((no-LF-at-eol-re "\\(?:\\\\.*\n\\)?")) - (concat "^\\(?:-.*\n\\)+" no-LF-at-eol-re - "\\(\\)" - "\\(?:\\+.*\n\\)+" no-LF-at-eol-re))) - end t) - (smerge-refine-subst (match-beginning 0) (match-end 1) - (match-end 1) (match-end 0) - nil 'diff-refine-preproc props-r props-a))) + (while (re-search-forward "^-" end t) + (let ((beg-del (progn (beginning-of-line) (point))) + beg-add end-add) + (when (and (diff--forward-while-leading-char ?- end) + ;; Allow for "\ No newline at end of file". + (progn (diff--forward-while-leading-char ?\\ end) + (setq beg-add (point))) + (diff--forward-while-leading-char ?+ end) + (progn (diff--forward-while-leading-char ?\\ end) + (setq end-add (point)))) + (smerge-refine-subst beg-del beg-add beg-add end-add + nil 'diff-refine-preproc props-r props-a))))) (`context (let* ((middle (save-excursion (re-search-forward "^---"))) (other middle)) commit 5304cb983146fe55ea1b4963ffc756ca4325673d Author: Eli Zaretskii Date: Wed Jan 18 20:36:14 2017 +0200 Remove lock file when auto-saving into the visited file * src/fileio.c (write_region): When auto-saving into the visited file, unlock the file whenever we mark the buffer unmodified. (Bug#25470) diff --git a/src/fileio.c b/src/fileio.c index be52d0f3d0..8c8cba9e49 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -5142,19 +5142,26 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename, if (! ok) report_file_errno ("Write error", filename, save_errno); + bool auto_saving_into_visited_file = + auto_saving + && ! NILP (Fstring_equal (BVAR (current_buffer, filename), + BVAR (current_buffer, auto_save_file_name))); if (visiting) { SAVE_MODIFF = MODIFF; XSETFASTINT (BVAR (current_buffer, save_length), Z - BEG); bset_filename (current_buffer, visit_file); update_mode_lines = 14; + if (auto_saving_into_visited_file) + unlock_file (lockname); } else if (quietly) { - if (auto_saving - && ! NILP (Fstring_equal (BVAR (current_buffer, filename), - BVAR (current_buffer, auto_save_file_name)))) - SAVE_MODIFF = MODIFF; + if (auto_saving_into_visited_file) + { + SAVE_MODIFF = MODIFF; + unlock_file (lockname); + } return Qnil; } commit 5fefaaa8c0696ba4b7b6e1e89267aa10fff88b31 Author: Eli Zaretskii Date: Wed Jan 18 18:06:42 2017 +0200 Fix a bug with signaling a thread that waits for condvar * src/thread.c (lisp_mutex_lock_for_thread): New function, with all the guts of lisp_mutex_lock. (lisp_mutex_lock): Call lisp_mutex_lock_for_thread. (condition_wait_callback): Don't call post_acquire_global_lock before locking the mutex, as that could cause a signaled thread to exit prematurely, because the condvar's mutex is recorded to be not owned by any thread, and with-mutex wants to unlock it as part of unwinding the stack in response to the signal. diff --git a/src/thread.c b/src/thread.c index 6048516659..9ea7e121a8 100644 --- a/src/thread.c +++ b/src/thread.c @@ -128,11 +128,11 @@ lisp_mutex_init (lisp_mutex_t *mutex) sys_cond_init (&mutex->condition); } -/* Lock MUTEX setting its count to COUNT, if non-zero, or to 1 - otherwise. +/* Lock MUTEX for thread LOCKER, setting its lock count to COUNT, if + non-zero, or to 1 otherwise. - If MUTEX is locked by the current thread, COUNT must be zero, and - the MUTEX's lock count will be incremented. + If MUTEX is locked by LOCKER, COUNT must be zero, and the MUTEX's + lock count will be incremented. If MUTEX is locked by another thread, this function will release the global lock, giving other threads a chance to run, and will @@ -143,24 +143,25 @@ lisp_mutex_init (lisp_mutex_t *mutex) unlocked (meaning other threads could have run during the wait), zero otherwise. */ static int -lisp_mutex_lock (lisp_mutex_t *mutex, int new_count) +lisp_mutex_lock_for_thread (lisp_mutex_t *mutex, struct thread_state *locker, + int new_count) { struct thread_state *self; if (mutex->owner == NULL) { - mutex->owner = current_thread; + mutex->owner = locker; mutex->count = new_count == 0 ? 1 : new_count; return 0; } - if (mutex->owner == current_thread) + if (mutex->owner == locker) { eassert (new_count == 0); ++mutex->count; return 0; } - self = current_thread; + self = locker; self->wait_condvar = &mutex->condition; while (mutex->owner != NULL && (new_count != 0 || NILP (self->error_symbol))) @@ -176,6 +177,12 @@ lisp_mutex_lock (lisp_mutex_t *mutex, int new_count) return 1; } +static int +lisp_mutex_lock (lisp_mutex_t *mutex, int new_count) +{ + return lisp_mutex_lock_for_thread (mutex, current_thread, new_count); +} + /* Decrement MUTEX's lock count. If the lock count becomes zero after decrementing it, meaning the mutex is now unlocked, broadcast that to all the threads that might be waiting to lock the mutex. This @@ -398,16 +405,16 @@ condition_wait_callback (void *arg) self->wait_condvar = NULL; } self->event_object = Qnil; - /* Since sys_cond_wait could switch threads, we need to re-establish - ourselves as the current thread, otherwise lisp_mutex_lock will - record the wrong thread as the owner of the mutex lock. */ - post_acquire_global_lock (self); - /* Calling lisp_mutex_lock might yield to other threads while this - one waits for the mutex to become unlocked, so we need to - announce us as the current thread by calling + /* Since sys_cond_wait could switch threads, we need to lock the + mutex for the thread which was the current when we were called, + otherwise lisp_mutex_lock will record the wrong thread as the + owner of the mutex lock. */ + lisp_mutex_lock_for_thread (&mutex->mutex, self, saved_count); + /* Calling lisp_mutex_lock_for_thread might yield to other threads + while this one waits for the mutex to become unlocked, so we need + to announce us as the current thread by calling post_acquire_global_lock. */ - if (lisp_mutex_lock (&mutex->mutex, saved_count)) - post_acquire_global_lock (self); + post_acquire_global_lock (self); } DEFUN ("condition-wait", Fcondition_wait, Scondition_wait, 1, 1, 0, commit 571532605bc0db221c76e36067435e4355e0d1a1 Author: Eli Zaretskii Date: Wed Jan 18 18:00:16 2017 +0200 Rudimentary error handling for non-main threads * src/thread.c (last_thread_error): New static variable. (syms_of_threads): Staticpro it. (record_thread_error, Fthread_last_error): New functions. (syms_of_threads): Defsubr Fthread_last_error. * doc/lispref/threads.texi (Basic Thread Functions): Document thread-last-error. * test/src/thread-tests.el (thread-errors, thread-signal-early) (threads-condvar-wait): Test the values returned by thread-last-error. diff --git a/doc/lispref/threads.texi b/doc/lispref/threads.texi index d6cf99d233..71742f576e 100644 --- a/doc/lispref/threads.texi +++ b/doc/lispref/threads.texi @@ -127,6 +127,17 @@ Return a list of all the live thread objects. A new list is returned by each invocation. @end defun +When code run by a thread signals an error that is unhandled, the +thread exits. Other threads can access the error form which caused +the thread to exit using the following function. + +@defun thread-last-error +This function returns the last error form recorded when a thread +exited due to an error. Each thread that exits abnormally overwrites +the form stored by the previous thread's error with a new value, so +only the last one can be accessed. +@end defun + @node Mutexes @section Mutexes diff --git a/src/thread.c b/src/thread.c index 5498fe5efc..6048516659 100644 --- a/src/thread.c +++ b/src/thread.c @@ -663,10 +663,13 @@ invoke_thread_function (void) return unbind_to (count, Qnil); } +static Lisp_Object last_thread_error; + static Lisp_Object -do_nothing (Lisp_Object whatever) +record_thread_error (Lisp_Object error_form) { - return whatever; + last_thread_error = error_form; + return error_form; } static void * @@ -695,7 +698,7 @@ run_thread (void *state) handlerlist_sentinel->next = NULL; /* It might be nice to do something with errors here. */ - internal_condition_case (invoke_thread_function, Qt, do_nothing); + internal_condition_case (invoke_thread_function, Qt, record_thread_error); update_processes_for_thread_death (Fcurrent_thread ()); @@ -944,6 +947,13 @@ DEFUN ("all-threads", Fall_threads, Sall_threads, 0, 0, 0, return result; } +DEFUN ("thread-last-error", Fthread_last_error, Sthread_last_error, 0, 0, 0, + doc: /* Return the last error form recorded by a dying thread. */) + (void) +{ + return last_thread_error; +} + bool @@ -1028,6 +1038,10 @@ syms_of_threads (void) defsubr (&Scondition_notify); defsubr (&Scondition_mutex); defsubr (&Scondition_name); + defsubr (&Sthread_last_error); + + staticpro (&last_thread_error); + last_thread_error = Qnil; } DEFSYM (Qthreadp, "threadp"); diff --git a/test/src/thread-tests.el b/test/src/thread-tests.el index df8222a21a..849b2e3dd1 100644 --- a/test/src/thread-tests.el +++ b/test/src/thread-tests.el @@ -222,8 +222,15 @@ (ert-deftest thread-errors () "Test what happens when a thread signals an error." - (should (threadp (make-thread #'call-error "call-error"))) - (should (threadp (make-thread #'thread-custom "thread-custom")))) + (let (th1 th2) + (setq th1 (make-thread #'call-error "call-error")) + (should (threadp th1)) + (while (thread-alive-p th1) + (thread-yield)) + (should (equal (thread-last-error) + '(error "Error is called"))) + (setq th2 (make-thread #'thread-custom "thread-custom")) + (should (threadp th2)))) (ert-deftest thread-sticky-point () "Test bug #25165 with point movement in cloned buffer." @@ -242,7 +249,8 @@ (while t (thread-yield)))))) (thread-signal thread 'error nil) (sit-for 1) - (should-not (thread-alive-p thread)))) + (should-not (thread-alive-p thread)) + (should (equal (thread-last-error) '(error))))) (defvar threads-condvar nil) @@ -287,6 +295,7 @@ (thread-signal new-thread 'error '("Die, die, die!")) (sleep-for 0.1) ;; Make sure the thread died. - (should (= (length (all-threads)) 1)))) + (should (= (length (all-threads)) 1)) + (should (equal (thread-last-error) '(error "Die, die, die!"))))) ;;; threads.el ends here