aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Albinus2018-08-30 21:29:04 +0200
committerMichael Albinus2018-08-30 21:29:04 +0200
commit54b92132e1ec16565d59d6d9f8ff8910f38843b2 (patch)
tree894a20a1c835c5da28d0b74a26667efed5aac189
parent3cc42bb60099c32f64e57d2ee33c8321adba7942 (diff)
downloademacs-54b92132e1ec16565d59d6d9f8ff8910f38843b2.tar.gz
emacs-54b92132e1ec16565d59d6d9f8ff8910f38843b2.zip
Handle thread-signal towards the main thread (Bug#32502)
* doc/lispref/threads.texi (Basic Thread Functions): * etc/NEWS: Document thread-signal towards the main thread. * lisp/emacs-lisp/thread.el: New package. * src/keyboard.c (read_char): Check for Qthread_event. (kbd_buffer_get_event, make_lispy_event): Handle THREAD_EVENT. (syms_of_keyboard): Declare Qthread_event. (keys_of_keyboard): Add thread-handle-event to special-event-map. * src/termhooks.h (enum event_kind): Add THREAD_EVENT. * src/thread.c: Include "keyboard.h". (poll_suppress_count) Don't declare extern. (Fthread_signal): Raise event if THREAD is the main thread. (Bug#32502) * test/src/thread-tests.el (thread): Require it. (threads-signal-main-thread): New test.
-rw-r--r--doc/lispref/threads.texi10
-rw-r--r--etc/NEWS4
-rw-r--r--lisp/emacs-lisp/thread.el42
-rw-r--r--src/keyboard.c27
-rw-r--r--src/termhooks.h4
-rw-r--r--src/thread.c33
-rw-r--r--test/src/thread-tests.el21
7 files changed, 123 insertions, 18 deletions
diff --git a/doc/lispref/threads.texi b/doc/lispref/threads.texi
index 58a3a918efd..98301984114 100644
--- a/doc/lispref/threads.texi
+++ b/doc/lispref/threads.texi
@@ -88,14 +88,8 @@ If @var{thread} was blocked by a call to @code{mutex-lock},
88@code{condition-wait}, or @code{thread-join}; @code{thread-signal} 88@code{condition-wait}, or @code{thread-join}; @code{thread-signal}
89will unblock it. 89will unblock it.
90 90
91Since signal handlers in Emacs are located in the main thread, a 91If @var{thread} is the main thread, the signal is not propagated
92signal must be propagated there in order to become visible. The 92there. Instead, it is shown as message in the main thread.
93second @code{signal} call let the thread die:
94
95@example
96(thread-signal main-thread 'error data)
97(signal 'error data)
98@end example
99@end defun 93@end defun
100 94
101@defun thread-yield 95@defun thread-yield
diff --git a/etc/NEWS b/etc/NEWS
index 8a774d81c5b..d536faaa2d6 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -726,6 +726,10 @@ to signal the main thread, e.g., when they encounter an error.
726+++ 726+++
727*** 'thread-join' returns the result of the finished thread now. 727*** 'thread-join' returns the result of the finished thread now.
728 728
729+++
730*** 'thread-signal' does not propagate errors to the main thread.
731Instead, error messages are just printed in the main thread.
732
729--- 733---
730** thingatpt.el supports a new "thing" called 'uuid'. 734** thingatpt.el supports a new "thing" called 'uuid'.
731A symbol 'uuid' can be passed to thing-at-point and it returns the 735A symbol 'uuid' can be passed to thing-at-point and it returns the
diff --git a/lisp/emacs-lisp/thread.el b/lisp/emacs-lisp/thread.el
new file mode 100644
index 00000000000..02cf9b9e53f
--- /dev/null
+++ b/lisp/emacs-lisp/thread.el
@@ -0,0 +1,42 @@
1;;; thread.el --- List active threads in a buffer -*- lexical-binding: t -*-
2
3;; Copyright (C) 2018 Free Software Foundation, Inc.
4
5;; Author: Gemini Lasswell <gazally@runbox.com>
6;; Maintainer: emacs-devel@gnu.org
7;; Keywords: lisp, tools, maint
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
23
24;;; Commentary:
25
26;;; Code:
27
28;;;###autoload
29(defun thread-handle-event (event)
30 "Handle thread events, propagated by `thread-signal'.
31An EVENT has the format
32 (thread-event THREAD ERROR-SYMBOL DATA)"
33 (interactive "e")
34 (if (and (consp event)
35 (eq (car event) 'thread-event)
36 (= (length event) 4))
37 (let ((thread (cadr event))
38 (err (cddr event)))
39 (message "Error %s: %S" thread err))))
40
41(provide 'thread)
42;;; thread.el ends here
diff --git a/src/keyboard.c b/src/keyboard.c
index 7fafb41fcc5..008d3b9d7c0 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -2828,6 +2828,9 @@ read_char (int commandflag, Lisp_Object map,
2828#ifdef USE_FILE_NOTIFY 2828#ifdef USE_FILE_NOTIFY
2829 || EQ (XCAR (c), Qfile_notify) 2829 || EQ (XCAR (c), Qfile_notify)
2830#endif 2830#endif
2831#ifdef THREADS_ENABLED
2832 || EQ (XCAR (c), Qthread_event)
2833#endif
2831 || EQ (XCAR (c), Qconfig_changed_event)) 2834 || EQ (XCAR (c), Qconfig_changed_event))
2832 && !end_time) 2835 && !end_time)
2833 /* We stopped being idle for this event; undo that. This 2836 /* We stopped being idle for this event; undo that. This
@@ -3739,7 +3742,7 @@ kbd_buffer_get_event (KBOARD **kbp,
3739 } 3742 }
3740#endif /* subprocesses */ 3743#endif /* subprocesses */
3741 3744
3742#if !defined HAVE_DBUS && !defined USE_FILE_NOTIFY 3745#if !defined HAVE_DBUS && !defined USE_FILE_NOTIFY && !defined THREADS_ENABLED
3743 if (noninteractive 3746 if (noninteractive
3744 /* In case we are running as a daemon, only do this before 3747 /* In case we are running as a daemon, only do this before
3745 detaching from the terminal. */ 3748 detaching from the terminal. */
@@ -3750,7 +3753,7 @@ kbd_buffer_get_event (KBOARD **kbp,
3750 *kbp = current_kboard; 3753 *kbp = current_kboard;
3751 return obj; 3754 return obj;
3752 } 3755 }
3753#endif /* !defined HAVE_DBUS && !defined USE_FILE_NOTIFY */ 3756#endif /* !defined HAVE_DBUS && !defined USE_FILE_NOTIFY && !defined THREADS_ENABLED */
3754 3757
3755 /* Wait until there is input available. */ 3758 /* Wait until there is input available. */
3756 for (;;) 3759 for (;;)
@@ -3900,6 +3903,9 @@ kbd_buffer_get_event (KBOARD **kbp,
3900#ifdef HAVE_DBUS 3903#ifdef HAVE_DBUS
3901 case DBUS_EVENT: 3904 case DBUS_EVENT:
3902#endif 3905#endif
3906#ifdef THREADS_ENABLED
3907 case THREAD_EVENT:
3908#endif
3903#ifdef HAVE_XWIDGETS 3909#ifdef HAVE_XWIDGETS
3904 case XWIDGET_EVENT: 3910 case XWIDGET_EVENT:
3905#endif 3911#endif
@@ -5983,6 +5989,13 @@ make_lispy_event (struct input_event *event)
5983 } 5989 }
5984#endif /* HAVE_DBUS */ 5990#endif /* HAVE_DBUS */
5985 5991
5992#ifdef THREADS_ENABLED
5993 case THREAD_EVENT:
5994 {
5995 return Fcons (Qthread_event, event->arg);
5996 }
5997#endif /* THREADS_ENABLED */
5998
5986#ifdef HAVE_XWIDGETS 5999#ifdef HAVE_XWIDGETS
5987 case XWIDGET_EVENT: 6000 case XWIDGET_EVENT:
5988 { 6001 {
@@ -11078,6 +11091,10 @@ syms_of_keyboard (void)
11078 DEFSYM (Qdbus_event, "dbus-event"); 11091 DEFSYM (Qdbus_event, "dbus-event");
11079#endif 11092#endif
11080 11093
11094#ifdef THREADS_ENABLED
11095 DEFSYM (Qthread_event, "thread-event");
11096#endif
11097
11081#ifdef HAVE_XWIDGETS 11098#ifdef HAVE_XWIDGETS
11082 DEFSYM (Qxwidget_event, "xwidget-event"); 11099 DEFSYM (Qxwidget_event, "xwidget-event");
11083#endif 11100#endif
@@ -11929,6 +11946,12 @@ keys_of_keyboard (void)
11929 "dbus-handle-event"); 11946 "dbus-handle-event");
11930#endif 11947#endif
11931 11948
11949#ifdef THREADS_ENABLED
11950 /* Define a special event which is raised for thread signals. */
11951 initial_define_lispy_key (Vspecial_event_map, "thread-event",
11952 "thread-handle-event");
11953#endif
11954
11932#ifdef USE_FILE_NOTIFY 11955#ifdef USE_FILE_NOTIFY
11933 /* Define a special event which is raised for notification callback 11956 /* Define a special event which is raised for notification callback
11934 functions. */ 11957 functions. */
diff --git a/src/termhooks.h b/src/termhooks.h
index 160bd2f4803..8b5f648b43d 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -222,6 +222,10 @@ enum event_kind
222 , DBUS_EVENT 222 , DBUS_EVENT
223#endif 223#endif
224 224
225#ifdef THREADS_ENABLED
226 , THREAD_EVENT
227#endif
228
225 , CONFIG_CHANGED_EVENT 229 , CONFIG_CHANGED_EVENT
226 230
227#ifdef HAVE_NTGUI 231#ifdef HAVE_NTGUI
diff --git a/src/thread.c b/src/thread.c
index 1c73d938655..78cb2161993 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -25,6 +25,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
25#include "process.h" 25#include "process.h"
26#include "coding.h" 26#include "coding.h"
27#include "syssignal.h" 27#include "syssignal.h"
28#include "keyboard.h"
28 29
29static struct thread_state main_thread; 30static struct thread_state main_thread;
30 31
@@ -34,7 +35,6 @@ static struct thread_state *all_threads = &main_thread;
34 35
35static sys_mutex_t global_lock; 36static sys_mutex_t global_lock;
36 37
37extern int poll_suppress_count;
38extern volatile int interrupt_input_blocked; 38extern volatile int interrupt_input_blocked;
39 39
40 40
@@ -863,7 +863,8 @@ DEFUN ("thread-signal", Fthread_signal, Sthread_signal, 3, 3, 0,
863This acts like `signal', but arranges for the signal to be raised 863This acts like `signal', but arranges for the signal to be raised
864in THREAD. If THREAD is the current thread, acts just like `signal'. 864in THREAD. If THREAD is the current thread, acts just like `signal'.
865This will interrupt a blocked call to `mutex-lock', `condition-wait', 865This will interrupt a blocked call to `mutex-lock', `condition-wait',
866or `thread-join' in the target thread. */) 866or `thread-join' in the target thread.
867If THREAD is the main thread, just the error message is shown. */)
867 (Lisp_Object thread, Lisp_Object error_symbol, Lisp_Object data) 868 (Lisp_Object thread, Lisp_Object error_symbol, Lisp_Object data)
868{ 869{
869 struct thread_state *tstate; 870 struct thread_state *tstate;
@@ -874,13 +875,29 @@ or `thread-join' in the target thread. */)
874 if (tstate == current_thread) 875 if (tstate == current_thread)
875 Fsignal (error_symbol, data); 876 Fsignal (error_symbol, data);
876 877
877 /* What to do if thread is already signaled? */ 878 if (main_thread_p (tstate))
878 /* What if error_symbol is Qnil? */ 879 {
879 tstate->error_symbol = error_symbol; 880 /* Construct an event. */
880 tstate->error_data = data; 881 struct input_event event;
882 EVENT_INIT (event);
883 event.kind = THREAD_EVENT;
884 event.frame_or_window = Qnil;
885 event.arg = list3 (Fcurrent_thread (), error_symbol, data);
886
887 /* Store it into the input event queue. */
888 kbd_buffer_store_event (&event);
889 }
890
891 else
892 {
893 /* What to do if thread is already signaled? */
894 /* What if error_symbol is Qnil? */
895 tstate->error_symbol = error_symbol;
896 tstate->error_data = data;
881 897
882 if (tstate->wait_condvar) 898 if (tstate->wait_condvar)
883 flush_stack_call_func (thread_signal_callback, tstate); 899 flush_stack_call_func (thread_signal_callback, tstate);
900 }
884 901
885 return Qnil; 902 return Qnil;
886} 903}
diff --git a/test/src/thread-tests.el b/test/src/thread-tests.el
index 364f6d61f05..cc1dff8a281 100644
--- a/test/src/thread-tests.el
+++ b/test/src/thread-tests.el
@@ -19,6 +19,8 @@
19 19
20;;; Code: 20;;; Code:
21 21
22(require 'thread)
23
22;; Declare the functions in case Emacs has been configured --without-threads. 24;; Declare the functions in case Emacs has been configured --without-threads.
23(declare-function all-threads "thread.c" ()) 25(declare-function all-threads "thread.c" ())
24(declare-function condition-mutex "thread.c" (cond)) 26(declare-function condition-mutex "thread.c" (cond))
@@ -320,6 +322,25 @@
320 (should-not (thread-alive-p thread)) 322 (should-not (thread-alive-p thread))
321 (should (equal (thread-last-error) '(error))))) 323 (should (equal (thread-last-error) '(error)))))
322 324
325(ert-deftest threads-signal-main-thread ()
326 "Test signaling the main thread."
327 (skip-unless (featurep 'threads))
328 ;; We cannot use `ert-with-message-capture', because threads do not
329 ;; know let-bound variables.
330 (with-current-buffer "*Messages*"
331 (let (buffer-read-only)
332 (erase-buffer))
333 (let ((thread
334 (make-thread #'(lambda () (thread-signal main-thread 'error nil)))))
335 (while (thread-alive-p thread)
336 (thread-yield))
337 (read-event nil nil 0.1)
338 ;; No error has been raised, which is part of the test.
339 (should
340 (string-match
341 (format-message "Error %s: (error nil)" thread)
342 (buffer-string ))))))
343
323(defvar threads-condvar nil) 344(defvar threads-condvar nil)
324 345
325(defun threads-test-condvar-wait () 346(defun threads-test-condvar-wait ()