aboutsummaryrefslogtreecommitdiffstats
path: root/src/thread.c
diff options
context:
space:
mode:
authorDmitry Gutov2025-08-09 22:40:07 +0300
committerDmitry Gutov2025-08-09 22:40:07 +0300
commit07eb39f1132ceb15e80e7912445890faa8f5b78c (patch)
treee6ab9c32a334fca746e68a9ac62728e312cfcd1c /src/thread.c
parentc4af4b39018923fdfc22b515acc1f6ba3f2b024d (diff)
downloademacs-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.c106
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
885DEFUN ("make-thread", Fmake_thread, Smake_thread, 1, 2, 0, 885DEFUN ("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.
887When the function exits, the thread dies. 887When the function exits, the thread dies.
888If NAME is given, it must be a string; it names the new thread. */) 888If NAME is given, it must be a string; it names the new thread.
889 (Lisp_Object function, Lisp_Object name) 889
890BUFFER-DISPOSITION determines how attached the thread is to its current
891buffer. If the value is t, that buffer can't be killed. Any other
892value, including nil (the default), means that if its buffer is killed,
893the thread is switched to another buffer and receives an error signal
894`thread-buffer-killed'. But if the value is symbol `silently', no error
895will 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
984static void
985thread_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
975DEFUN ("thread-signal", Fthread_signal, Sthread_signal, 3, 3, 0, 993DEFUN ("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.
977This acts like `signal', but arranges for the signal to be raised 995This 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
1048DEFUN ("thread-buffer-disposition", Fthread_buffer_disposition, Sthread_buffer_disposition,
1049 1, 1, 0,
1050 doc: /* Return the value of THREAD's buffer disposition.
1051See `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
1062DEFUN ("thread-set-buffer-disposition", Fthread_set_buffer_disposition, Sthread_set_buffer_disposition,
1063 2, 2, 0,
1064 doc: /* Set THREAD's buffer disposition.
1065See `make-thread' for the description of possible values.
1066
1067Buffer 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
1033DEFUN ("thread--blocker", Fthread_blocker, Sthread_blocker, 1, 1, 0, 1083DEFUN ("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.
1035If THREAD is blocked in `thread-join' on a second thread, return that 1085If 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
1199void
1200thread_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
1151bool 1231bool
@@ -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