diff options
| author | Eli Zaretskii | 2017-10-04 10:27:49 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2017-10-04 10:27:49 +0300 |
| commit | ea39d470bf35e45f1d8e39795f06ac74b3c37fc7 (patch) | |
| tree | 406dbe320ffd5c9e2a31fd1b8568e119e50e9279 /src | |
| parent | fdbaebde08f4e53e3fc06fae99398c68a4e285fb (diff) | |
| download | emacs-ea39d470bf35e45f1d8e39795f06ac74b3c37fc7.tar.gz emacs-ea39d470bf35e45f1d8e39795f06ac74b3c37fc7.zip | |
Avoid crashes on C-g when several threads wait for input
* src/thread.h (m_getcjmp): New member of 'struct thread_state'.
(getcjmp): Define to current thread's 'm_getcjmp'.
* src/thread.c (maybe_reacquire_global_lock): Switch to main
thread, since this is called from a SIGINT handler, which always
runs in the context of the main thread.
* src/lisp.h (sys_jmp_buf, sys_setjmp, sys_longjmp): Move the
definitions before thread.h is included, as thread.h now uses
sys_jmp_buf.
* src/keyboard.c (getcjmp): Remove declaration.
(read_char): Don't call maybe_reacquire_global_lock here.
(handle_interrupt): Call maybe_reacquire_global_lock here, if
invoked from the SIGINT handler, to make sure
quit_throw_to_read_char runs with main thread's Lisp bindings and
uses the main thread's jmp_buf buffer. (Bug#28630)
Diffstat (limited to 'src')
| -rw-r--r-- | src/keyboard.c | 14 | ||||
| -rw-r--r-- | src/lisp.h | 39 | ||||
| -rw-r--r-- | src/thread.c | 16 | ||||
| -rw-r--r-- | src/thread.h | 7 |
4 files changed, 45 insertions, 31 deletions
diff --git a/src/keyboard.c b/src/keyboard.c index e8701b88708..ee353d2b078 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -145,10 +145,6 @@ static Lisp_Object recover_top_level_message; | |||
| 145 | /* Message normally displayed by Vtop_level. */ | 145 | /* Message normally displayed by Vtop_level. */ |
| 146 | static Lisp_Object regular_top_level_message; | 146 | static Lisp_Object regular_top_level_message; |
| 147 | 147 | ||
| 148 | /* For longjmp to where kbd input is being done. */ | ||
| 149 | |||
| 150 | static sys_jmp_buf getcjmp; | ||
| 151 | |||
| 152 | /* True while displaying for echoing. Delays C-g throwing. */ | 148 | /* True while displaying for echoing. Delays C-g throwing. */ |
| 153 | 149 | ||
| 154 | static bool echoing; | 150 | static bool echoing; |
| @@ -2570,9 +2566,6 @@ read_char (int commandflag, Lisp_Object map, | |||
| 2570 | so restore it now. */ | 2566 | so restore it now. */ |
| 2571 | restore_getcjmp (save_jump); | 2567 | restore_getcjmp (save_jump); |
| 2572 | pthread_sigmask (SIG_SETMASK, &empty_mask, 0); | 2568 | pthread_sigmask (SIG_SETMASK, &empty_mask, 0); |
| 2573 | #if THREADS_ENABLED | ||
| 2574 | maybe_reacquire_global_lock (); | ||
| 2575 | #endif | ||
| 2576 | unbind_to (jmpcount, Qnil); | 2569 | unbind_to (jmpcount, Qnil); |
| 2577 | XSETINT (c, quit_char); | 2570 | XSETINT (c, quit_char); |
| 2578 | internal_last_event_frame = selected_frame; | 2571 | internal_last_event_frame = selected_frame; |
| @@ -10508,6 +10501,13 @@ handle_interrupt (bool in_signal_handler) | |||
| 10508 | outside of polling since we don't get SIGIO like X and we don't have a | 10501 | outside of polling since we don't get SIGIO like X and we don't have a |
| 10509 | separate event loop thread like W32. */ | 10502 | separate event loop thread like W32. */ |
| 10510 | #ifndef HAVE_NS | 10503 | #ifndef HAVE_NS |
| 10504 | #ifdef THREADS_ENABLED | ||
| 10505 | /* If we were called from a signal handler, we must be in the main | ||
| 10506 | thread, see deliver_process_signal. So we must make sure the | ||
| 10507 | main thread holds the global lock. */ | ||
| 10508 | if (in_signal_handler) | ||
| 10509 | maybe_reacquire_global_lock (); | ||
| 10510 | #endif | ||
| 10511 | if (waiting_for_input && !echoing) | 10511 | if (waiting_for_input && !echoing) |
| 10512 | quit_throw_to_read_char (in_signal_handler); | 10512 | quit_throw_to_read_char (in_signal_handler); |
| 10513 | #endif | 10513 | #endif |
diff --git a/src/lisp.h b/src/lisp.h index 680c25d4c49..bdb162aea4c 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -1865,6 +1865,26 @@ verify (offsetof (struct Lisp_Sub_Char_Table, contents) | |||
| 1865 | == (offsetof (struct Lisp_Vector, contents) | 1865 | == (offsetof (struct Lisp_Vector, contents) |
| 1866 | + SUB_CHAR_TABLE_OFFSET * sizeof (Lisp_Object))); | 1866 | + SUB_CHAR_TABLE_OFFSET * sizeof (Lisp_Object))); |
| 1867 | 1867 | ||
| 1868 | |||
| 1869 | /* Save and restore the instruction and environment pointers, | ||
| 1870 | without affecting the signal mask. */ | ||
| 1871 | |||
| 1872 | #ifdef HAVE__SETJMP | ||
| 1873 | typedef jmp_buf sys_jmp_buf; | ||
| 1874 | # define sys_setjmp(j) _setjmp (j) | ||
| 1875 | # define sys_longjmp(j, v) _longjmp (j, v) | ||
| 1876 | #elif defined HAVE_SIGSETJMP | ||
| 1877 | typedef sigjmp_buf sys_jmp_buf; | ||
| 1878 | # define sys_setjmp(j) sigsetjmp (j, 0) | ||
| 1879 | # define sys_longjmp(j, v) siglongjmp (j, v) | ||
| 1880 | #else | ||
| 1881 | /* A platform that uses neither _longjmp nor siglongjmp; assume | ||
| 1882 | longjmp does not affect the sigmask. */ | ||
| 1883 | typedef jmp_buf sys_jmp_buf; | ||
| 1884 | # define sys_setjmp(j) setjmp (j) | ||
| 1885 | # define sys_longjmp(j, v) longjmp (j, v) | ||
| 1886 | #endif | ||
| 1887 | |||
| 1868 | #include "thread.h" | 1888 | #include "thread.h" |
| 1869 | 1889 | ||
| 1870 | /*********************************************************************** | 1890 | /*********************************************************************** |
| @@ -3003,25 +3023,6 @@ extern void defvar_kboard (struct Lisp_Kboard_Objfwd *, const char *, int); | |||
| 3003 | static struct Lisp_Kboard_Objfwd ko_fwd; \ | 3023 | static struct Lisp_Kboard_Objfwd ko_fwd; \ |
| 3004 | defvar_kboard (&ko_fwd, lname, offsetof (KBOARD, vname ## _)); \ | 3024 | defvar_kboard (&ko_fwd, lname, offsetof (KBOARD, vname ## _)); \ |
| 3005 | } while (false) | 3025 | } while (false) |
| 3006 | |||
| 3007 | /* Save and restore the instruction and environment pointers, | ||
| 3008 | without affecting the signal mask. */ | ||
| 3009 | |||
| 3010 | #ifdef HAVE__SETJMP | ||
| 3011 | typedef jmp_buf sys_jmp_buf; | ||
| 3012 | # define sys_setjmp(j) _setjmp (j) | ||
| 3013 | # define sys_longjmp(j, v) _longjmp (j, v) | ||
| 3014 | #elif defined HAVE_SIGSETJMP | ||
| 3015 | typedef sigjmp_buf sys_jmp_buf; | ||
| 3016 | # define sys_setjmp(j) sigsetjmp (j, 0) | ||
| 3017 | # define sys_longjmp(j, v) siglongjmp (j, v) | ||
| 3018 | #else | ||
| 3019 | /* A platform that uses neither _longjmp nor siglongjmp; assume | ||
| 3020 | longjmp does not affect the sigmask. */ | ||
| 3021 | typedef jmp_buf sys_jmp_buf; | ||
| 3022 | # define sys_setjmp(j) setjmp (j) | ||
| 3023 | # define sys_longjmp(j, v) longjmp (j, v) | ||
| 3024 | #endif | ||
| 3025 | 3026 | ||
| 3026 | 3027 | ||
| 3027 | /* Elisp uses several stacks: | 3028 | /* Elisp uses several stacks: |
diff --git a/src/thread.c b/src/thread.c index 42d7791ad0f..d075bdb3a13 100644 --- a/src/thread.c +++ b/src/thread.c | |||
| @@ -101,14 +101,20 @@ acquire_global_lock (struct thread_state *self) | |||
| 101 | post_acquire_global_lock (self); | 101 | post_acquire_global_lock (self); |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | /* This is called from keyboard.c when it detects that SIGINT | 104 | /* This is called from keyboard.c when it detects that SIGINT was |
| 105 | interrupted thread_select before the current thread could acquire | 105 | delivered to the main thread and interrupted thread_select before |
| 106 | the lock. We must acquire the lock to prevent a thread from | 106 | the main thread could acquire the lock. We must acquire the lock |
| 107 | running without holding the global lock, and to avoid repeated | 107 | to prevent a thread from running without holding the global lock, |
| 108 | calls to sys_mutex_unlock, which invokes undefined behavior. */ | 108 | and to avoid repeated calls to sys_mutex_unlock, which invokes |
| 109 | undefined behavior. */ | ||
| 109 | void | 110 | void |
| 110 | maybe_reacquire_global_lock (void) | 111 | maybe_reacquire_global_lock (void) |
| 111 | { | 112 | { |
| 113 | /* SIGINT handler is always run on the main thread, see | ||
| 114 | deliver_process_signal, so reflect that in our thread-tracking | ||
| 115 | variables. */ | ||
| 116 | current_thread = &main_thread; | ||
| 117 | |||
| 112 | if (current_thread->not_holding_lock) | 118 | if (current_thread->not_holding_lock) |
| 113 | { | 119 | { |
| 114 | struct thread_state *self = current_thread; | 120 | struct thread_state *self = current_thread; |
diff --git a/src/thread.h b/src/thread.h index 7fce8674f0e..cb2133d72d4 100644 --- a/src/thread.h +++ b/src/thread.h | |||
| @@ -158,6 +158,13 @@ struct thread_state | |||
| 158 | bool m_waiting_for_input; | 158 | bool m_waiting_for_input; |
| 159 | #define waiting_for_input (current_thread->m_waiting_for_input) | 159 | #define waiting_for_input (current_thread->m_waiting_for_input) |
| 160 | 160 | ||
| 161 | /* For longjmp to where kbd input is being done. This is per-thread | ||
| 162 | so that if more than one thread calls read_char, they don't | ||
| 163 | clobber each other's getcjmp, which will cause | ||
| 164 | quit_throw_to_read_char crash due to using a wrong stack. */ | ||
| 165 | sys_jmp_buf m_getcjmp; | ||
| 166 | #define getcjmp (current_thread->m_getcjmp) | ||
| 167 | |||
| 161 | /* The OS identifier for this thread. */ | 168 | /* The OS identifier for this thread. */ |
| 162 | sys_thread_t thread_id; | 169 | sys_thread_t thread_id; |
| 163 | 170 | ||