diff options
| author | Eli Zaretskii | 2017-01-18 18:00:16 +0200 |
|---|---|---|
| committer | Eli Zaretskii | 2017-01-18 18:00:16 +0200 |
| commit | 571532605bc0db221c76e36067435e4355e0d1a1 (patch) | |
| tree | 5a8120468d552a682c5bbfc1ec9f83c80d364b11 | |
| parent | dbb29d7eb428dd53617d31a9cc159d889deb1e8e (diff) | |
| download | emacs-571532605bc0db221c76e36067435e4355e0d1a1.tar.gz emacs-571532605bc0db221c76e36067435e4355e0d1a1.zip | |
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.
| -rw-r--r-- | doc/lispref/threads.texi | 11 | ||||
| -rw-r--r-- | src/thread.c | 20 | ||||
| -rw-r--r-- | test/src/thread-tests.el | 17 |
3 files changed, 41 insertions, 7 deletions
diff --git a/doc/lispref/threads.texi b/doc/lispref/threads.texi index d6cf99d2332..71742f576e5 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 | |||
| 127 | by each invocation. | 127 | by each invocation. |
| 128 | @end defun | 128 | @end defun |
| 129 | 129 | ||
| 130 | When code run by a thread signals an error that is unhandled, the | ||
| 131 | thread exits. Other threads can access the error form which caused | ||
| 132 | the thread to exit using the following function. | ||
| 133 | |||
| 134 | @defun thread-last-error | ||
| 135 | This function returns the last error form recorded when a thread | ||
| 136 | exited due to an error. Each thread that exits abnormally overwrites | ||
| 137 | the form stored by the previous thread's error with a new value, so | ||
| 138 | only the last one can be accessed. | ||
| 139 | @end defun | ||
| 140 | |||
| 130 | @node Mutexes | 141 | @node Mutexes |
| 131 | @section Mutexes | 142 | @section Mutexes |
| 132 | 143 | ||
diff --git a/src/thread.c b/src/thread.c index 5498fe5efcb..6048516659e 100644 --- a/src/thread.c +++ b/src/thread.c | |||
| @@ -663,10 +663,13 @@ invoke_thread_function (void) | |||
| 663 | return unbind_to (count, Qnil); | 663 | return unbind_to (count, Qnil); |
| 664 | } | 664 | } |
| 665 | 665 | ||
| 666 | static Lisp_Object last_thread_error; | ||
| 667 | |||
| 666 | static Lisp_Object | 668 | static Lisp_Object |
| 667 | do_nothing (Lisp_Object whatever) | 669 | record_thread_error (Lisp_Object error_form) |
| 668 | { | 670 | { |
| 669 | return whatever; | 671 | last_thread_error = error_form; |
| 672 | return error_form; | ||
| 670 | } | 673 | } |
| 671 | 674 | ||
| 672 | static void * | 675 | static void * |
| @@ -695,7 +698,7 @@ run_thread (void *state) | |||
| 695 | handlerlist_sentinel->next = NULL; | 698 | handlerlist_sentinel->next = NULL; |
| 696 | 699 | ||
| 697 | /* It might be nice to do something with errors here. */ | 700 | /* It might be nice to do something with errors here. */ |
| 698 | internal_condition_case (invoke_thread_function, Qt, do_nothing); | 701 | internal_condition_case (invoke_thread_function, Qt, record_thread_error); |
| 699 | 702 | ||
| 700 | update_processes_for_thread_death (Fcurrent_thread ()); | 703 | update_processes_for_thread_death (Fcurrent_thread ()); |
| 701 | 704 | ||
| @@ -944,6 +947,13 @@ DEFUN ("all-threads", Fall_threads, Sall_threads, 0, 0, 0, | |||
| 944 | return result; | 947 | return result; |
| 945 | } | 948 | } |
| 946 | 949 | ||
| 950 | DEFUN ("thread-last-error", Fthread_last_error, Sthread_last_error, 0, 0, 0, | ||
| 951 | doc: /* Return the last error form recorded by a dying thread. */) | ||
| 952 | (void) | ||
| 953 | { | ||
| 954 | return last_thread_error; | ||
| 955 | } | ||
| 956 | |||
| 947 | 957 | ||
| 948 | 958 | ||
| 949 | bool | 959 | bool |
| @@ -1028,6 +1038,10 @@ syms_of_threads (void) | |||
| 1028 | defsubr (&Scondition_notify); | 1038 | defsubr (&Scondition_notify); |
| 1029 | defsubr (&Scondition_mutex); | 1039 | defsubr (&Scondition_mutex); |
| 1030 | defsubr (&Scondition_name); | 1040 | defsubr (&Scondition_name); |
| 1041 | defsubr (&Sthread_last_error); | ||
| 1042 | |||
| 1043 | staticpro (&last_thread_error); | ||
| 1044 | last_thread_error = Qnil; | ||
| 1031 | } | 1045 | } |
| 1032 | 1046 | ||
| 1033 | DEFSYM (Qthreadp, "threadp"); | 1047 | DEFSYM (Qthreadp, "threadp"); |
diff --git a/test/src/thread-tests.el b/test/src/thread-tests.el index df8222a21aa..849b2e3dd1b 100644 --- a/test/src/thread-tests.el +++ b/test/src/thread-tests.el | |||
| @@ -222,8 +222,15 @@ | |||
| 222 | 222 | ||
| 223 | (ert-deftest thread-errors () | 223 | (ert-deftest thread-errors () |
| 224 | "Test what happens when a thread signals an error." | 224 | "Test what happens when a thread signals an error." |
| 225 | (should (threadp (make-thread #'call-error "call-error"))) | 225 | (let (th1 th2) |
| 226 | (should (threadp (make-thread #'thread-custom "thread-custom")))) | 226 | (setq th1 (make-thread #'call-error "call-error")) |
| 227 | (should (threadp th1)) | ||
| 228 | (while (thread-alive-p th1) | ||
| 229 | (thread-yield)) | ||
| 230 | (should (equal (thread-last-error) | ||
| 231 | '(error "Error is called"))) | ||
| 232 | (setq th2 (make-thread #'thread-custom "thread-custom")) | ||
| 233 | (should (threadp th2)))) | ||
| 227 | 234 | ||
| 228 | (ert-deftest thread-sticky-point () | 235 | (ert-deftest thread-sticky-point () |
| 229 | "Test bug #25165 with point movement in cloned buffer." | 236 | "Test bug #25165 with point movement in cloned buffer." |
| @@ -242,7 +249,8 @@ | |||
| 242 | (while t (thread-yield)))))) | 249 | (while t (thread-yield)))))) |
| 243 | (thread-signal thread 'error nil) | 250 | (thread-signal thread 'error nil) |
| 244 | (sit-for 1) | 251 | (sit-for 1) |
| 245 | (should-not (thread-alive-p thread)))) | 252 | (should-not (thread-alive-p thread)) |
| 253 | (should (equal (thread-last-error) '(error))))) | ||
| 246 | 254 | ||
| 247 | (defvar threads-condvar nil) | 255 | (defvar threads-condvar nil) |
| 248 | 256 | ||
| @@ -287,6 +295,7 @@ | |||
| 287 | (thread-signal new-thread 'error '("Die, die, die!")) | 295 | (thread-signal new-thread 'error '("Die, die, die!")) |
| 288 | (sleep-for 0.1) | 296 | (sleep-for 0.1) |
| 289 | ;; Make sure the thread died. | 297 | ;; Make sure the thread died. |
| 290 | (should (= (length (all-threads)) 1)))) | 298 | (should (= (length (all-threads)) 1)) |
| 299 | (should (equal (thread-last-error) '(error "Die, die, die!"))))) | ||
| 291 | 300 | ||
| 292 | ;;; threads.el ends here | 301 | ;;; threads.el ends here |