* 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.
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
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 *
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 ());
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;
+}
+
\f
bool
defsubr (&Scondition_notify);
defsubr (&Scondition_mutex);
defsubr (&Scondition_name);
+ defsubr (&Sthread_last_error);
+
+ staticpro (&last_thread_error);
+ last_thread_error = Qnil;
}
DEFSYM (Qthreadp, "threadp");
(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."
(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)
(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