diff options
| author | Dmitry Gutov | 2025-08-09 22:40:07 +0300 |
|---|---|---|
| committer | Dmitry Gutov | 2025-08-09 22:40:07 +0300 |
| commit | 07eb39f1132ceb15e80e7912445890faa8f5b78c (patch) | |
| tree | e6ab9c32a334fca746e68a9ac62728e312cfcd1c /src/thread.c | |
| parent | c4af4b39018923fdfc22b515acc1f6ba3f2b024d (diff) | |
| download | emacs-07eb39f1132ceb15e80e7912445890faa8f5b78c.tar.gz emacs-07eb39f1132ceb15e80e7912445890faa8f5b78c.zip | |
Allow thread's buffer to be killed, by default
* src/thread.c (Fmake_thread): Add new argument (bug#76969).
(thread_set_error): New function, extracted from thread-signal.
(Fthread_buffer_disposition): Add getter.
(Fthread_set_buffer_disposition): And setter.
(thread_check_current_buffer): Check the values of threads'
buffer_disposition.
(thread_all_before_buffer_killed): New function.
(init_threads): Set buffer_disposition to nil for the main thread.
(syms_of_threads): Add new symbols and define the error.
* src/thread.h (thread_state): New field buffer_disposition.
(thread_all_before_buffer_killed): Declare.
* src/buffer.c (Fkill_buffer): Call thread_check_current_buffer
one more time after all hooks and after that call
thread_all_before_buffer_killed.
* src/comp.c (ABI_VERSION): Increase the value.
* test/src/thread-tests.el (thread-buffer-disposition-t)
(thread-buffer-disposition-nil)
(thread-buffer-disposition-silently)
(thread-set-buffer-disposition)
(thread-set-buffer-disposition-main-thread): New tests.
* doc/lispref/threads.texi (Basic Thread Functions): Document
buffer-disposition in make-thread and its getter and setter.
* etc/NEWS: Add entry.
Diffstat (limited to 'src/thread.c')
| -rw-r--r-- | src/thread.c | 106 |
1 files changed, 98 insertions, 8 deletions
diff --git a/src/thread.c b/src/thread.c index 8fd713d0c81..3d29f6c591a 100644 --- a/src/thread.c +++ b/src/thread.c | |||
| @@ -882,11 +882,18 @@ finalize_one_thread (struct thread_state *state) | |||
| 882 | free_bc_thread (&state->bc); | 882 | free_bc_thread (&state->bc); |
| 883 | } | 883 | } |
| 884 | 884 | ||
| 885 | DEFUN ("make-thread", Fmake_thread, Smake_thread, 1, 2, 0, | 885 | DEFUN ("make-thread", Fmake_thread, Smake_thread, 1, 3, 0, |
| 886 | doc: /* Start a new thread and run FUNCTION in it. | 886 | doc: /* Start a new thread and run FUNCTION in it. |
| 887 | When the function exits, the thread dies. | 887 | When the function exits, the thread dies. |
| 888 | If NAME is given, it must be a string; it names the new thread. */) | 888 | If NAME is given, it must be a string; it names the new thread. |
| 889 | (Lisp_Object function, Lisp_Object name) | 889 | |
| 890 | BUFFER-DISPOSITION determines how attached the thread is to its current | ||
| 891 | buffer. If the value is t, that buffer can't be killed. Any other | ||
| 892 | value, including nil (the default), means that if its buffer is killed, | ||
| 893 | the thread is switched to another buffer and receives an error signal | ||
| 894 | `thread-buffer-killed'. But if the value is symbol `silently', no error | ||
| 895 | will be signaled. */) | ||
| 896 | (Lisp_Object function, Lisp_Object name, Lisp_Object buffer_disposition) | ||
| 890 | { | 897 | { |
| 891 | /* Can't start a thread in temacs. */ | 898 | /* Can't start a thread in temacs. */ |
| 892 | if (!initialized) | 899 | if (!initialized) |
| @@ -934,6 +941,8 @@ If NAME is given, it must be a string; it names the new thread. */) | |||
| 934 | #endif | 941 | #endif |
| 935 | } | 942 | } |
| 936 | 943 | ||
| 944 | new_thread->buffer_disposition = buffer_disposition; | ||
| 945 | |||
| 937 | /* FIXME: race here where new thread might not be filled in? */ | 946 | /* FIXME: race here where new thread might not be filled in? */ |
| 938 | Lisp_Object result; | 947 | Lisp_Object result; |
| 939 | XSETTHREAD (result, new_thread); | 948 | XSETTHREAD (result, new_thread); |
| @@ -972,6 +981,15 @@ thread_signal_callback (void *arg) | |||
| 972 | post_acquire_global_lock (self); | 981 | post_acquire_global_lock (self); |
| 973 | } | 982 | } |
| 974 | 983 | ||
| 984 | static void | ||
| 985 | thread_set_error (struct thread_state *tstate, Lisp_Object error_symbol, Lisp_Object data) | ||
| 986 | { | ||
| 987 | /* What to do if thread is already signaled? */ | ||
| 988 | /* What if error_symbol is Qnil? */ | ||
| 989 | tstate->error_symbol = error_symbol; | ||
| 990 | tstate->error_data = data; | ||
| 991 | } | ||
| 992 | |||
| 975 | DEFUN ("thread-signal", Fthread_signal, Sthread_signal, 3, 3, 0, | 993 | DEFUN ("thread-signal", Fthread_signal, Sthread_signal, 3, 3, 0, |
| 976 | doc: /* Signal an error in a thread. | 994 | doc: /* Signal an error in a thread. |
| 977 | This acts like `signal', but arranges for the signal to be raised | 995 | This acts like `signal', but arranges for the signal to be raised |
| @@ -1006,10 +1024,7 @@ If THREAD is the main thread, just the error message is shown. */) | |||
| 1006 | else | 1024 | else |
| 1007 | #endif | 1025 | #endif |
| 1008 | { | 1026 | { |
| 1009 | /* What to do if thread is already signaled? */ | 1027 | thread_set_error (tstate, error_symbol, data); |
| 1010 | /* What if error_symbol is Qnil? */ | ||
| 1011 | tstate->error_symbol = error_symbol; | ||
| 1012 | tstate->error_data = data; | ||
| 1013 | 1028 | ||
| 1014 | if (tstate->wait_condvar) | 1029 | if (tstate->wait_condvar) |
| 1015 | flush_stack_call_func (thread_signal_callback, tstate); | 1030 | flush_stack_call_func (thread_signal_callback, tstate); |
| @@ -1030,6 +1045,41 @@ DEFUN ("thread-live-p", Fthread_live_p, Sthread_live_p, 1, 1, 0, | |||
| 1030 | return thread_live_p (tstate) ? Qt : Qnil; | 1045 | return thread_live_p (tstate) ? Qt : Qnil; |
| 1031 | } | 1046 | } |
| 1032 | 1047 | ||
| 1048 | DEFUN ("thread-buffer-disposition", Fthread_buffer_disposition, Sthread_buffer_disposition, | ||
| 1049 | 1, 1, 0, | ||
| 1050 | doc: /* Return the value of THREAD's buffer disposition. | ||
| 1051 | See `make-thread' for the description of possible values. */) | ||
| 1052 | (Lisp_Object thread) | ||
| 1053 | { | ||
| 1054 | struct thread_state *tstate; | ||
| 1055 | |||
| 1056 | CHECK_THREAD (thread); | ||
| 1057 | tstate = XTHREAD (thread); | ||
| 1058 | |||
| 1059 | return tstate->buffer_disposition; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | DEFUN ("thread-set-buffer-disposition", Fthread_set_buffer_disposition, Sthread_set_buffer_disposition, | ||
| 1063 | 2, 2, 0, | ||
| 1064 | doc: /* Set THREAD's buffer disposition. | ||
| 1065 | See `make-thread' for the description of possible values. | ||
| 1066 | |||
| 1067 | Buffer disposition of the main thread cannot be modified. */) | ||
| 1068 | (Lisp_Object thread, Lisp_Object value) | ||
| 1069 | { | ||
| 1070 | struct thread_state *tstate; | ||
| 1071 | |||
| 1072 | CHECK_THREAD (thread); | ||
| 1073 | tstate = XTHREAD (thread); | ||
| 1074 | |||
| 1075 | if (main_thread_p (tstate)) | ||
| 1076 | CHECK_TYPE (NILP (value), Qnull, value); | ||
| 1077 | |||
| 1078 | tstate->buffer_disposition = value; | ||
| 1079 | |||
| 1080 | return value; | ||
| 1081 | } | ||
| 1082 | |||
| 1033 | DEFUN ("thread--blocker", Fthread_blocker, Sthread_blocker, 1, 1, 0, | 1083 | DEFUN ("thread--blocker", Fthread_blocker, Sthread_blocker, 1, 1, 0, |
| 1034 | doc: /* Return the object that THREAD is blocking on. | 1084 | doc: /* Return the object that THREAD is blocking on. |
| 1035 | If THREAD is blocked in `thread-join' on a second thread, return that | 1085 | If THREAD is blocked in `thread-join' on a second thread, return that |
| @@ -1139,13 +1189,43 @@ thread_check_current_buffer (struct buffer *buffer) | |||
| 1139 | if (iter == current_thread) | 1189 | if (iter == current_thread) |
| 1140 | continue; | 1190 | continue; |
| 1141 | 1191 | ||
| 1142 | if (iter->m_current_buffer == buffer) | 1192 | if (iter->m_current_buffer == buffer && EQ (iter->buffer_disposition, Qt)) |
| 1143 | return true; | 1193 | return true; |
| 1144 | } | 1194 | } |
| 1145 | 1195 | ||
| 1146 | return false; | 1196 | return false; |
| 1147 | } | 1197 | } |
| 1148 | 1198 | ||
| 1199 | void | ||
| 1200 | thread_all_before_buffer_killed (Lisp_Object current) | ||
| 1201 | { | ||
| 1202 | struct thread_state *iter; | ||
| 1203 | struct buffer * other = NULL; | ||
| 1204 | struct buffer * b = XBUFFER (current); | ||
| 1205 | struct thread_state *caller_thread = current_thread; | ||
| 1206 | |||
| 1207 | for (iter = all_threads; iter; iter = iter->next_thread) | ||
| 1208 | { | ||
| 1209 | if (iter == caller_thread) | ||
| 1210 | continue; | ||
| 1211 | |||
| 1212 | if (iter->m_current_buffer == b) | ||
| 1213 | { | ||
| 1214 | Lisp_Object thread; | ||
| 1215 | |||
| 1216 | XSETTHREAD (thread, iter); | ||
| 1217 | |||
| 1218 | if (other == NULL) | ||
| 1219 | other = XBUFFER (Fother_buffer (current, Qnil, Qnil)); | ||
| 1220 | |||
| 1221 | if (!EQ (iter->buffer_disposition, Qsilently)) | ||
| 1222 | thread_set_error (iter, Qthread_buffer_killed, Qnil); | ||
| 1223 | |||
| 1224 | iter->m_current_buffer = other; | ||
| 1225 | } | ||
| 1226 | } | ||
| 1227 | } | ||
| 1228 | |||
| 1149 | 1229 | ||
| 1150 | 1230 | ||
| 1151 | bool | 1231 | bool |
| @@ -1174,6 +1254,7 @@ init_threads (void) | |||
| 1174 | #endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ | 1254 | #endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ |
| 1175 | 1255 | ||
| 1176 | main_thread.s.thread_id = sys_thread_self (); | 1256 | main_thread.s.thread_id = sys_thread_self (); |
| 1257 | main_thread.s.buffer_disposition = Qnil; | ||
| 1177 | init_bc_thread (&main_thread.s.bc); | 1258 | init_bc_thread (&main_thread.s.bc); |
| 1178 | } | 1259 | } |
| 1179 | 1260 | ||
| @@ -1203,6 +1284,8 @@ syms_of_threads (void) | |||
| 1203 | defsubr (&Scondition_mutex); | 1284 | defsubr (&Scondition_mutex); |
| 1204 | defsubr (&Scondition_name); | 1285 | defsubr (&Scondition_name); |
| 1205 | defsubr (&Sthread_last_error); | 1286 | defsubr (&Sthread_last_error); |
| 1287 | defsubr (&Sthread_buffer_disposition); | ||
| 1288 | defsubr (&Sthread_set_buffer_disposition); | ||
| 1206 | 1289 | ||
| 1207 | staticpro (&last_thread_error); | 1290 | staticpro (&last_thread_error); |
| 1208 | last_thread_error = Qnil; | 1291 | last_thread_error = Qnil; |
| @@ -1214,6 +1297,13 @@ syms_of_threads (void) | |||
| 1214 | DEFSYM (Qmutexp, "mutexp"); | 1297 | DEFSYM (Qmutexp, "mutexp"); |
| 1215 | DEFSYM (Qcondition_variable_p, "condition-variable-p"); | 1298 | DEFSYM (Qcondition_variable_p, "condition-variable-p"); |
| 1216 | 1299 | ||
| 1300 | DEFSYM (Qthread_buffer_killed, "thread-buffer-killed"); | ||
| 1301 | Fput (Qthread_buffer_killed, Qerror_conditions, | ||
| 1302 | list (Qthread_buffer_killed, Qerror)); | ||
| 1303 | Fput (Qthread_buffer_killed, Qerror_message, | ||
| 1304 | build_string ("Thread's current buffer killed")); | ||
| 1305 | DEFSYM (Qsilently, "silently"); | ||
| 1306 | |||
| 1217 | DEFVAR_LISP ("main-thread", Vmain_thread, | 1307 | DEFVAR_LISP ("main-thread", Vmain_thread, |
| 1218 | doc: /* The main thread of Emacs. */); | 1308 | doc: /* The main thread of Emacs. */); |
| 1219 | #ifdef THREADS_ENABLED | 1309 | #ifdef THREADS_ENABLED |