aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEli Zaretskii2016-12-19 19:11:16 +0200
committerEli Zaretskii2016-12-19 19:11:16 +0200
commitfe3188b1cecc7ac5534616c8edf14a84b1b3bbb0 (patch)
tree39041b114c679f98255e9c95f2b4666d6f269ab2
parent657bcaf5ac30449915e070c3fa80a2eaaf1ee7e1 (diff)
downloademacs-fe3188b1cecc7ac5534616c8edf14a84b1b3bbb0.tar.gz
emacs-fe3188b1cecc7ac5534616c8edf14a84b1b3bbb0.zip
Fix crashes upon C-g on Posix TTY frames
* src/thread.h (struct thread_state): New member not_holding_lock. (maybe_reacquire_global_lock): Add prototype. * src/thread.c: Include syssignal.h. (maybe_reacquire_global_lock): New function. (really_call_select): Set the not_holding_lock member of the thread state before releasing the lock, and rest it after re-acquiring the lock when the select function returns. Block SIGINT while doing this to make sure we are not interrupted on TTY frames. * src/sysdep.c (block_interrupt_signal, restore_signal_mask): New functions. * src/syssignal.h (block_interrupt_signal, restore_signal_mask): Add prototypes. * src/keyboard.c (read_char) [THREADS_ENABLED]: Call maybe_reacquire_global_lock. (Bug#25178)
-rw-r--r--src/keyboard.c3
-rw-r--r--src/sysdep.c17
-rw-r--r--src/syssignal.h2
-rw-r--r--src/thread.c27
-rw-r--r--src/thread.h8
5 files changed, 57 insertions, 0 deletions
diff --git a/src/keyboard.c b/src/keyboard.c
index 1fb1d492ce6..f2ee313b8c9 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -2571,6 +2571,9 @@ read_char (int commandflag, Lisp_Object map,
2571 so restore it now. */ 2571 so restore it now. */
2572 restore_getcjmp (save_jump); 2572 restore_getcjmp (save_jump);
2573 pthread_sigmask (SIG_SETMASK, &empty_mask, 0); 2573 pthread_sigmask (SIG_SETMASK, &empty_mask, 0);
2574#if THREADS_ENABLED
2575 maybe_reacquire_global_lock ();
2576#endif
2574 unbind_to (jmpcount, Qnil); 2577 unbind_to (jmpcount, Qnil);
2575 XSETINT (c, quit_char); 2578 XSETINT (c, quit_char);
2576 internal_last_event_frame = selected_frame; 2579 internal_last_event_frame = selected_frame;
diff --git a/src/sysdep.c b/src/sysdep.c
index 3d2b9bdeeee..96c9e538409 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -765,6 +765,23 @@ unblock_child_signal (sigset_t const *oldset)
765 pthread_sigmask (SIG_SETMASK, oldset, 0); 765 pthread_sigmask (SIG_SETMASK, oldset, 0);
766} 766}
767 767
768/* Block SIGINT. */
769void
770block_interrupt_signal (sigset_t *oldset)
771{
772 sigset_t blocked;
773 sigemptyset (&blocked);
774 sigaddset (&blocked, SIGINT);
775 pthread_sigmask (SIG_BLOCK, &blocked, oldset);
776}
777
778/* Restore previously saved signal mask. */
779void
780restore_signal_mask (sigset_t const *oldset)
781{
782 pthread_sigmask (SIG_SETMASK, oldset, 0);
783}
784
768#endif /* !MSDOS */ 785#endif /* !MSDOS */
769 786
770/* Saving and restoring the process group of Emacs's terminal. */ 787/* Saving and restoring the process group of Emacs's terminal. */
diff --git a/src/syssignal.h b/src/syssignal.h
index 3de83c71759..62704fc351e 100644
--- a/src/syssignal.h
+++ b/src/syssignal.h
@@ -25,6 +25,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
25extern void init_signals (bool); 25extern void init_signals (bool);
26extern void block_child_signal (sigset_t *); 26extern void block_child_signal (sigset_t *);
27extern void unblock_child_signal (sigset_t const *); 27extern void unblock_child_signal (sigset_t const *);
28extern void block_interrupt_signal (sigset_t *);
29extern void restore_signal_mask (sigset_t const *);
28extern void block_tty_out_signal (sigset_t *); 30extern void block_tty_out_signal (sigset_t *);
29extern void unblock_tty_out_signal (sigset_t const *); 31extern void unblock_tty_out_signal (sigset_t const *);
30 32
diff --git a/src/thread.c b/src/thread.c
index e8cb430119f..bf2cf1b06c8 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -24,6 +24,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
24#include "buffer.h" 24#include "buffer.h"
25#include "process.h" 25#include "process.h"
26#include "coding.h" 26#include "coding.h"
27#include "syssignal.h"
27 28
28static struct thread_state primary_thread; 29static struct thread_state primary_thread;
29 30
@@ -100,6 +101,23 @@ acquire_global_lock (struct thread_state *self)
100 post_acquire_global_lock (self); 101 post_acquire_global_lock (self);
101} 102}
102 103
104/* This is called from keyboard.c when it detects that SIGINT
105 interrupted thread_select before the current thread could acquire
106 the lock. We must acquire the lock to prevent a thread from
107 running without holding the global lock, and to avoid repeated
108 calls to sys_mutex_unlock, which invokes undefined behavior. */
109void
110maybe_reacquire_global_lock (void)
111{
112 if (current_thread->not_holding_lock)
113 {
114 struct thread_state *self = current_thread;
115
116 acquire_global_lock (self);
117 current_thread->not_holding_lock = 0;
118 }
119}
120
103 121
104 122
105static void 123static void
@@ -493,11 +511,20 @@ really_call_select (void *arg)
493{ 511{
494 struct select_args *sa = arg; 512 struct select_args *sa = arg;
495 struct thread_state *self = current_thread; 513 struct thread_state *self = current_thread;
514 sigset_t oldset;
496 515
516 block_interrupt_signal (&oldset);
517 self->not_holding_lock = 1;
497 release_global_lock (); 518 release_global_lock ();
519 restore_signal_mask (&oldset);
520
498 sa->result = (sa->func) (sa->max_fds, sa->rfds, sa->wfds, sa->efds, 521 sa->result = (sa->func) (sa->max_fds, sa->rfds, sa->wfds, sa->efds,
499 sa->timeout, sa->sigmask); 522 sa->timeout, sa->sigmask);
523
524 block_interrupt_signal (&oldset);
500 acquire_global_lock (self); 525 acquire_global_lock (self);
526 self->not_holding_lock = 0;
527 restore_signal_mask (&oldset);
501} 528}
502 529
503int 530int
diff --git a/src/thread.h b/src/thread.h
index e6084b13c22..7dee67d6595 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -171,6 +171,13 @@ struct thread_state
171 interrupter should broadcast to this condition. */ 171 interrupter should broadcast to this condition. */
172 sys_cond_t *wait_condvar; 172 sys_cond_t *wait_condvar;
173 173
174 /* This thread might have released the global lock. If so, this is
175 non-zero. When a thread runs outside thread_select with this
176 flag non-zero, it means it has been interrupted by SIGINT while
177 in thread_select, and didn't have a chance of acquiring the lock.
178 It must do so ASAP. */
179 int not_holding_lock;
180
174 /* Threads are kept on a linked list. */ 181 /* Threads are kept on a linked list. */
175 struct thread_state *next_thread; 182 struct thread_state *next_thread;
176}; 183};
@@ -224,6 +231,7 @@ extern void unmark_threads (void);
224extern void finalize_one_thread (struct thread_state *state); 231extern void finalize_one_thread (struct thread_state *state);
225extern void finalize_one_mutex (struct Lisp_Mutex *); 232extern void finalize_one_mutex (struct Lisp_Mutex *);
226extern void finalize_one_condvar (struct Lisp_CondVar *); 233extern void finalize_one_condvar (struct Lisp_CondVar *);
234extern void maybe_reacquire_global_lock (void);
227 235
228extern void init_threads_once (void); 236extern void init_threads_once (void);
229extern void init_threads (void); 237extern void init_threads (void);