Subject: bug#32502: 27.0.50; Tramp; C-g during asynchronous remote find-file kills Emacs

From: Michael Albinus <[email-masked]>
Date: Thu, 30 Aug 2018 18:48:19 +0200

Michael Albinus <[email-masked]> writes:

>>> I'm just changing thread-signal such a way that it ignores silently
>>> signals sent to the main thread. Shall I implement this event injection
>>> instead?
>>
>> I don't know.  I guess it depends on how important is it to show the
>> errors nicely.  You could leave signals for now and use the events
>> idea later as an enhancement.
>
> I will see how easy it is. I have experience with dbus-event and
> file-notify-event; it shouldn't too hard for me to implement thread-event.
>
>>> And what shall be done with this event in the main thread? Just writing
>>> an error message, or some kind of handling?
>>
>> Yes, displaying a message would be a good starting point, I think.
>
> Since special events have their own handler, it will be simple to change
> later. Will start with echoing the error message.

Appended is a first implementation. Comments?

Best regards, Michael.

diff --git a/doc/lispref/threads.texi b/doc/lispref/threads.texi
index 58a3a918ef..9830198411 100644
--- a/doc/lispref/threads.texi
+++ b/doc/lispref/threads.texi
@@ -88,14 +88,8 @@ Basic Thread Functions
 @code{condition-wait}, or @code{thread-join}; @code{thread-signal}
 will unblock it.
 
-Since signal handlers in Emacs are located in the main thread, a
-signal must be propagated there in order to become visible.  The
-second @code{signal} call let the thread die:
-
-@example
-(thread-signal main-thread 'error data)
-(signal 'error data)
-@end example
+If @var{thread} is the main thread, the signal is not propagated
+there.  Instead, it is shown as message in the main thread.
 @end defun
 
 @defun thread-yield
diff --git a/src/keyboard.c b/src/keyboard.c
index 7fafb41fcc..008d3b9d7c 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -2827,6 +2827,9 @@ read_char (int commandflag, Lisp_Object map,
 #endif
 #ifdef USE_FILE_NOTIFY
 	      || EQ (XCAR (c), Qfile_notify)
+#endif
+#ifdef THREADS_ENABLED
+	      || EQ (XCAR (c), Qthread_event)
 #endif
 	      || EQ (XCAR (c), Qconfig_changed_event))
           && !end_time)
@@ -3739,7 +3742,7 @@ kbd_buffer_get_event (KBOARD **kbp,
     }
 #endif	/* subprocesses */
 
-#if !defined HAVE_DBUS && !defined USE_FILE_NOTIFY
+#if !defined HAVE_DBUS && !defined USE_FILE_NOTIFY && !defined THREADS_ENABLED
   if (noninteractive
       /* In case we are running as a daemon, only do this before
 	 detaching from the terminal.  */
@@ -3750,7 +3753,7 @@ kbd_buffer_get_event (KBOARD **kbp,
       *kbp = current_kboard;
       return obj;
     }
-#endif	/* !defined HAVE_DBUS && !defined USE_FILE_NOTIFY  */
+#endif	/* !defined HAVE_DBUS && !defined USE_FILE_NOTIFY && !defined THREADS_ENABLED  */
 
   /* Wait until there is input available.  */
   for (;;)
@@ -3900,6 +3903,9 @@ kbd_buffer_get_event (KBOARD **kbp,
 #ifdef HAVE_DBUS
       case DBUS_EVENT:
 #endif
+#ifdef THREADS_ENABLED
+      case THREAD_EVENT:
+#endif
 #ifdef HAVE_XWIDGETS
       case XWIDGET_EVENT:
 #endif
@@ -5983,6 +5989,13 @@ make_lispy_event (struct input_event *event)
       }
 #endif /* HAVE_DBUS */
 
+#ifdef THREADS_ENABLED
+    case THREAD_EVENT:
+      {
+	return Fcons (Qthread_event, event->arg);
+      }
+#endif /* THREADS_ENABLED */
+
 #ifdef HAVE_XWIDGETS
     case XWIDGET_EVENT:
       {
@@ -11078,6 +11091,10 @@ syms_of_keyboard (void)
   DEFSYM (Qdbus_event, "dbus-event");
 #endif
 
+#ifdef THREADS_ENABLED
+  DEFSYM (Qthread_event, "thread-event");
+#endif
+
 #ifdef HAVE_XWIDGETS
   DEFSYM (Qxwidget_event, "xwidget-event");
 #endif
@@ -11929,6 +11946,12 @@ keys_of_keyboard (void)
 			    "dbus-handle-event");
 #endif
 
+#ifdef THREADS_ENABLED
+  /* Define a special event which is raised for thread signals.  */
+  initial_define_lispy_key (Vspecial_event_map, "thread-event",
+			    "thread-handle-event");
+#endif
+
 #ifdef USE_FILE_NOTIFY
   /* Define a special event which is raised for notification callback
      functions.  */
diff --git a/src/termhooks.h b/src/termhooks.h
index 160bd2f480..8b5f648b43 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -222,6 +222,10 @@ enum event_kind
   , DBUS_EVENT
 #endif
 
+#ifdef THREADS_ENABLED
+  , THREAD_EVENT
+#endif
+
   , CONFIG_CHANGED_EVENT
 
 #ifdef HAVE_NTGUI
diff --git a/src/thread.c b/src/thread.c
index 1c73d93865..0234f65e02 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/>.  */
 #include "process.h"
 #include "coding.h"
 #include "syssignal.h"
+#include "keyboard.h"
 
 static struct thread_state main_thread;
 
@@ -34,7 +35,6 @@ static struct thread_state *all_threads = &main_thread;
 
 static sys_mutex_t global_lock;
 
-extern int poll_suppress_count;
 extern volatile int interrupt_input_blocked;
 
 
@@ -863,7 +863,8 @@ DEFUN ("thread-signal", Fthread_signal, Sthread_signal, 3, 3, 0,
 This acts like `signal', but arranges for the signal to be raised
 in THREAD.  If THREAD is the current thread, acts just like `signal'.
 This will interrupt a blocked call to `mutex-lock', `condition-wait',
-or `thread-join' in the target thread.  */)
+or `thread-join' in the target thread.
+Signals to the main thread are ignored quietly.  */)
   (Lisp_Object thread, Lisp_Object error_symbol, Lisp_Object data)
 {
   struct thread_state *tstate;
@@ -874,13 +875,29 @@ or `thread-join' in the target thread.  */)
   if (tstate == current_thread)
     Fsignal (error_symbol, data);
 
-  /* What to do if thread is already signaled?  */
-  /* What if error_symbol is Qnil?  */
-  tstate->error_symbol = error_symbol;
-  tstate->error_data = data;
+  if (main_thread_p (tstate))
+    {
+      /* Construct an event.  */
+      struct input_event event;
+      EVENT_INIT (event);
+      event.kind = THREAD_EVENT;
+      event.frame_or_window = Qnil;
+      event.arg = list3 (Fcurrent_thread (), error_symbol, data);
+
+      /* Store it into the input event queue.  */
+      kbd_buffer_store_event (&event);
+    }
+
+  else
+    {
+      /* What to do if thread is already signaled?  */
+      /* What if error_symbol is Qnil?  */
+      tstate->error_symbol = error_symbol;
+      tstate->error_data = data;
 
-  if (tstate->wait_condvar)
-    flush_stack_call_func (thread_signal_callback, tstate);
+      if (tstate->wait_condvar)
+	flush_stack_call_func (thread_signal_callback, tstate);
+    }
 
   return Qnil;
 }