]> git.eshelyaron.com Git - emacs.git/commitdiff
Avoid crashing if a new thread is signaled right away
authorEli Zaretskii <eliz@gnu.org>
Mon, 12 Dec 2016 17:08:21 +0000 (19:08 +0200)
committerEli Zaretskii <eliz@gnu.org>
Mon, 12 Dec 2016 17:08:21 +0000 (19:08 +0200)
* src/thread.c (post_acquire_global_lock): Don't raise the pending
signal if the thread's handlers were not yet set up, as that will
cause Emacs to exit with a fatal error.  This can happen if a
thread is signaled as soon as make-thread returns, before the new
thread had an opportunity to acquire the global lock, set up the
handlers, and call the thread function.

* test/src/thread-tests.el (thread-signal-early): New test.

src/thread.c
test/src/thread-tests.el

index 6e9ca2e256b6eb967ad794cb7a4c69da683c472e..e8cb430119f69e101cf2c278fe11cedb56843ad7 100644 (file)
@@ -77,7 +77,12 @@ post_acquire_global_lock (struct thread_state *self)
       set_buffer_internal_2 (current_buffer);
     }
 
-  if (!NILP (current_thread->error_symbol))
+   /* We could have been signaled while waiting to grab the global lock
+      for the first time since this thread was created, in which case
+      we didn't yet have the opportunity to set up the handlers.  Delay
+      raising the signal in that case (it will be actually raised when
+      the thread comes here after acquiring the lock the next time).  */
+  if (!NILP (current_thread->error_symbol) && handlerlist)
     {
       Lisp_Object sym = current_thread->error_symbol;
       Lisp_Object data = current_thread->error_data;
@@ -622,16 +627,15 @@ run_thread (void *state)
 
   acquire_global_lock (self);
 
-  { /* Put a dummy catcher at top-level so that handlerlist is never NULL.
-       This is important since handlerlist->nextfree holds the freelist
-       which would otherwise leak every time we unwind back to top-level.   */
-    handlerlist_sentinel = xzalloc (sizeof (struct handler));
-    handlerlist = handlerlist_sentinel->nextfree = handlerlist_sentinel;
-    struct handler *c = push_handler (Qunbound, CATCHER);
-    eassert (c == handlerlist_sentinel);
-    handlerlist_sentinel->nextfree = NULL;
-    handlerlist_sentinel->next = NULL;
-  }
+  /* Put a dummy catcher at top-level so that handlerlist is never NULL.
+     This is important since handlerlist->nextfree holds the freelist
+     which would otherwise leak every time we unwind back to top-level.   */
+  handlerlist_sentinel = xzalloc (sizeof (struct handler));
+  handlerlist = handlerlist_sentinel->nextfree = handlerlist_sentinel;
+  struct handler *c = push_handler (Qunbound, CATCHER);
+  eassert (c == handlerlist_sentinel);
+  handlerlist_sentinel->nextfree = NULL;
+  handlerlist_sentinel->next = NULL;
 
   /* It might be nice to do something with errors here.  */
   internal_condition_case (invoke_thread_function, Qt, do_nothing);
index 7261cda9fbb5955165cfc82e4ad0a5f5e0fc08da..26c0b725ff882c9f33b783a8761969408d3ba251 100644 (file)
     (sit-for 1)
     (should (= (point) 21))))
 
+(ert-deftest thread-signal-early ()
+  "Test signaling a thread as soon as it is started by the OS."
+  (let ((thread
+         (make-thread #'(lambda ()
+                          (while t (thread-yield))))))
+    (thread-signal thread 'error nil)
+    (sit-for 1)
+    (should-not (thread-alive-p thread))))
+
 ;;; threads.el ends here