diff options
| author | Eli Zaretskii | 2016-12-19 19:11:16 +0200 |
|---|---|---|
| committer | Eli Zaretskii | 2016-12-19 19:11:16 +0200 |
| commit | fe3188b1cecc7ac5534616c8edf14a84b1b3bbb0 (patch) | |
| tree | 39041b114c679f98255e9c95f2b4666d6f269ab2 | |
| parent | 657bcaf5ac30449915e070c3fa80a2eaaf1ee7e1 (diff) | |
| download | emacs-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.c | 3 | ||||
| -rw-r--r-- | src/sysdep.c | 17 | ||||
| -rw-r--r-- | src/syssignal.h | 2 | ||||
| -rw-r--r-- | src/thread.c | 27 | ||||
| -rw-r--r-- | src/thread.h | 8 |
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. */ | ||
| 769 | void | ||
| 770 | block_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. */ | ||
| 779 | void | ||
| 780 | restore_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/>. */ | |||
| 25 | extern void init_signals (bool); | 25 | extern void init_signals (bool); |
| 26 | extern void block_child_signal (sigset_t *); | 26 | extern void block_child_signal (sigset_t *); |
| 27 | extern void unblock_child_signal (sigset_t const *); | 27 | extern void unblock_child_signal (sigset_t const *); |
| 28 | extern void block_interrupt_signal (sigset_t *); | ||
| 29 | extern void restore_signal_mask (sigset_t const *); | ||
| 28 | extern void block_tty_out_signal (sigset_t *); | 30 | extern void block_tty_out_signal (sigset_t *); |
| 29 | extern void unblock_tty_out_signal (sigset_t const *); | 31 | extern 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 | ||
| 28 | static struct thread_state primary_thread; | 29 | static 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. */ | ||
| 109 | void | ||
| 110 | maybe_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 | ||
| 105 | static void | 123 | static 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 | ||
| 503 | int | 530 | int |
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); | |||
| 224 | extern void finalize_one_thread (struct thread_state *state); | 231 | extern void finalize_one_thread (struct thread_state *state); |
| 225 | extern void finalize_one_mutex (struct Lisp_Mutex *); | 232 | extern void finalize_one_mutex (struct Lisp_Mutex *); |
| 226 | extern void finalize_one_condvar (struct Lisp_CondVar *); | 233 | extern void finalize_one_condvar (struct Lisp_CondVar *); |
| 234 | extern void maybe_reacquire_global_lock (void); | ||
| 227 | 235 | ||
| 228 | extern void init_threads_once (void); | 236 | extern void init_threads_once (void); |
| 229 | extern void init_threads (void); | 237 | extern void init_threads (void); |