aboutsummaryrefslogtreecommitdiffstats
path: root/src/thread.c
diff options
context:
space:
mode:
authorTom Tromey2012-08-15 13:09:32 -0600
committerTom Tromey2012-08-15 13:09:32 -0600
commit1dcacbc64721b1a4de58aa36460b0a39e766be63 (patch)
tree98a07fba97225221d3bcfab970070b5a6a6564d6 /src/thread.c
parent60a9d2a7728895c1a5bfbc37c3bfa8fde35abe61 (diff)
downloademacs-1dcacbc64721b1a4de58aa36460b0a39e766be63.tar.gz
emacs-1dcacbc64721b1a4de58aa36460b0a39e766be63.zip
This adds most of the thread features visible to emacs lisp.
I roughly followed the Bordeaux threads API: http://trac.common-lisp.net/bordeaux-threads/wiki/ApiDocumentation ... but not identically. In particular I chose not to implement interrupt-thread or destroy-thread, but instead a thread-signalling approach. I'm still undecided about *default-special-bindings* (which I did not implement). I think it would be more emacs-like to capture the let bindings at make-thread time, but IIRC Stefan didn't like this idea the first time around. There are one or two semantics issues pointed out in the patch where I could use some advice.
Diffstat (limited to 'src/thread.c')
-rw-r--r--src/thread.c354
1 files changed, 346 insertions, 8 deletions
diff --git a/src/thread.c b/src/thread.c
index 7d2f81ec9ce..5da2e10f1ae 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -20,15 +20,70 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20#include <config.h> 20#include <config.h>
21#include <setjmp.h> 21#include <setjmp.h>
22#include "lisp.h" 22#include "lisp.h"
23#include "character.h"
24#include "buffer.h"
23 25
24struct thread_state the_only_thread; 26/* FIXME */
27extern void unbind_for_thread_switch (void);
28extern void rebind_for_thread_switch (void);
25 29
26struct thread_state *current_thread = &the_only_thread; 30static struct thread_state primary_thread;
27 31
28struct thread_state *all_threads = &the_only_thread; 32struct thread_state *current_thread = &primary_thread;
33
34static struct thread_state *all_threads = &primary_thread;
29 35
30sys_mutex_t global_lock; 36sys_mutex_t global_lock;
31 37
38Lisp_Object Qthreadp;
39
40
41
42static void
43release_global_lock (void)
44{
45 sys_mutex_unlock (&global_lock);
46}
47
48/* You must call this after acquiring the global lock.
49 acquire_global_lock does it for you. */
50void
51post_acquire_global_lock (struct thread_state *self)
52{
53 Lisp_Object buffer;
54
55 if (self != current_thread)
56 {
57 unbind_for_thread_switch ();
58 current_thread = self;
59 rebind_for_thread_switch ();
60 }
61
62 /* We need special handling to re-set the buffer. */
63 XSETBUFFER (buffer, self->m_current_buffer);
64 self->m_current_buffer = 0;
65 set_buffer_internal (XBUFFER (buffer));
66
67 if (!EQ (current_thread->error_symbol, Qnil))
68 {
69 Lisp_Object sym = current_thread->error_symbol;
70 Lisp_Object data = current_thread->error_data;
71
72 current_thread->error_symbol = Qnil;
73 current_thread->error_data = Qnil;
74 Fsignal (sym, data);
75 }
76}
77
78static void
79acquire_global_lock (struct thread_state *self)
80{
81 sys_mutex_lock (&global_lock);
82 post_acquire_global_lock (self);
83}
84
85
86
32static void 87static void
33mark_one_thread (struct thread_state *thread) 88mark_one_thread (struct thread_state *thread)
34{ 89{
@@ -113,19 +168,302 @@ unmark_threads (void)
113 unmark_byte_stack (iter->m_byte_stack_list); 168 unmark_byte_stack (iter->m_byte_stack_list);
114} 169}
115 170
171
172
173static void
174yield_callback (void *ignore)
175{
176 struct thread_state *self = current_thread;
177
178 release_global_lock ();
179 sys_thread_yield ();
180 acquire_global_lock (self);
181}
182
183DEFUN ("thread-yield", Fthread_yield, Sthread_yield, 0, 0, 0,
184 doc: /* Yield the CPU to another thread. */)
185 (void)
186{
187 flush_stack_call_func (yield_callback, NULL);
188 return Qnil;
189}
190
191static Lisp_Object
192invoke_thread_function (void)
193{
194 Lisp_Object iter;
195
196 int count = SPECPDL_INDEX ();
197
198 Ffuncall (1, &current_thread->function);
199 return unbind_to (count, Qnil);
200}
201
202static Lisp_Object
203do_nothing (Lisp_Object whatever)
204{
205 return whatever;
206}
207
208static void *
209run_thread (void *state)
210{
211 char stack_pos;
212 struct thread_state *self = state;
213 struct thread_state **iter;
214
215 self->m_stack_bottom = &stack_pos;
216 self->stack_top = self->m_stack_bottom = &stack_pos;
217 self->thread_id = sys_thread_self ();
218
219 acquire_global_lock (self);
220
221 /* It might be nice to do something with errors here. */
222 internal_condition_case (invoke_thread_function, Qt, do_nothing);
223
224 unbind_for_thread_switch ();
225
226 /* Unlink this thread from the list of all threads. */
227 for (iter = &all_threads; *iter != self; iter = &(*iter)->next_thread)
228 ;
229 *iter = (*iter)->next_thread;
230
231 self->m_last_thing_searched = Qnil;
232 self->m_saved_last_thing_searched = Qnil;
233 self->name = Qnil;
234 self->function = Qnil;
235 self->error_symbol = Qnil;
236 self->error_data = Qnil;
237 xfree (self->m_specpdl);
238 self->m_specpdl = NULL;
239 self->m_specpdl_ptr = NULL;
240 self->m_specpdl_size = 0;
241
242 sys_cond_broadcast (&self->thread_condvar);
243
244 release_global_lock ();
245
246 return NULL;
247}
248
116void 249void
117init_threads_once (void) 250finalize_one_thread (struct thread_state *state)
118{ 251{
119 the_only_thread.header.size 252 sys_cond_destroy (&state->thread_condvar);
253}
254
255DEFUN ("make-thread", Fmake_thread, Smake_thread, 1, 2, 0,
256 doc: /* Start a new thread and run FUNCTION in it.
257When the function exits, the thread dies.
258If NAME is given, it names the new thread. */)
259 (Lisp_Object function, Lisp_Object name)
260{
261 sys_thread_t thr;
262 struct thread_state *new_thread;
263 Lisp_Object result;
264
265 /* Can't start a thread in temacs. */
266 if (!initialized)
267 abort ();
268
269 new_thread = ALLOCATE_PSEUDOVECTOR (struct thread_state, m_gcprolist,
270 PVEC_THREAD);
271 memset ((char *) new_thread + offsetof (struct thread_state, m_gcprolist),
272 0, sizeof (struct thread_state) - offsetof (struct thread_state,
273 m_gcprolist));
274
275 new_thread->function = function;
276 new_thread->name = name;
277 new_thread->m_last_thing_searched = Qnil; /* copy from parent? */
278 new_thread->m_saved_last_thing_searched = Qnil;
279 new_thread->m_current_buffer = current_thread->m_current_buffer;
280 new_thread->error_symbol = Qnil;
281 new_thread->error_data = Qnil;
282
283 new_thread->m_specpdl_size = 50;
284 new_thread->m_specpdl = xmalloc (new_thread->m_specpdl_size
285 * sizeof (struct specbinding));
286 new_thread->m_specpdl_ptr = new_thread->m_specpdl;
287
288 sys_cond_init (&new_thread->thread_condvar);
289
290 /* We'll need locking here eventually. */
291 new_thread->next_thread = all_threads;
292 all_threads = new_thread;
293
294 if (! sys_thread_create (&thr, run_thread, new_thread))
295 {
296 /* Restore the previous situation. */
297 all_threads = all_threads->next_thread;
298 error ("Could not start a new thread");
299 }
300
301 /* FIXME: race here where new thread might not be filled in? */
302 XSETTHREAD (result, new_thread);
303 return result;
304}
305
306DEFUN ("current-thread", Fcurrent_thread, Scurrent_thread, 0, 0, 0,
307 doc: /* Return the current thread. */)
308 (void)
309{
310 Lisp_Object result;
311 XSETTHREAD (result, current_thread);
312 return result;
313}
314
315DEFUN ("thread-name", Fthread_name, Sthread_name, 1, 1, 0,
316 doc: /* Return the name of the THREAD.
317The name is the same object that was passed to `make-thread'. */)
318 (Lisp_Object thread)
319{
320 struct thread_state *tstate;
321
322 CHECK_THREAD (thread);
323 tstate = XTHREAD (thread);
324
325 return tstate->name;
326}
327
328static void
329thread_signal_callback (void *arg)
330{
331 struct thread_state *tstate = arg;
332 struct thread_state *self = current_thread;
333
334 sys_cond_broadcast (tstate->wait_condvar);
335 post_acquire_global_lock (self);
336}
337
338DEFUN ("thread-signal", Fthread_signal, Sthread_signal, 3, 3, 0,
339 doc: /* FIXME */)
340 (Lisp_Object thread, Lisp_Object error_symbol, Lisp_Object data)
341{
342 struct thread_state *tstate;
343
344 CHECK_THREAD (thread);
345 tstate = XTHREAD (thread);
346
347 if (tstate == current_thread)
348 Fsignal (error_symbol, data);
349
350 /* What to do if thread is already signalled? */
351 /* What if error_symbol is Qnil? */
352 tstate->error_symbol = error_symbol;
353 tstate->error_data = data;
354
355 if (tstate->wait_condvar)
356 flush_stack_call_func (thread_signal_callback, tstate);
357
358 return Qnil;
359}
360
361DEFUN ("thread-alive-p", Fthread_alive_p, Sthread_alive_p, 1, 1, 0,
362 doc: /* FIXME */)
363 (Lisp_Object thread)
364{
365 struct thread_state *tstate;
366
367 CHECK_THREAD (thread);
368 tstate = XTHREAD (thread);
369
370 /* m_specpdl is set when the thread is created and cleared when the
371 thread dies. */
372 return tstate->m_specpdl == NULL ? Qnil : Qt;
373}
374
375static void
376thread_join_callback (void *arg)
377{
378 struct thread_state *tstate = arg;
379 struct thread_state *self = current_thread;
380
381 self->wait_condvar = &tstate->thread_condvar;
382 while (tstate->m_specpdl != NULL && EQ (self->error_symbol, Qnil))
383 sys_cond_wait (self->wait_condvar, &global_lock);
384
385 self->wait_condvar = NULL;
386 post_acquire_global_lock (self);
387}
388
389DEFUN ("thread-join", Fthread_join, Sthread_join, 1, 1, 0,
390 doc: /* FIXME */)
391 (Lisp_Object thread)
392{
393 struct thread_state *tstate;
394
395 CHECK_THREAD (thread);
396 tstate = XTHREAD (thread);
397
398 if (tstate->m_specpdl != NULL)
399 flush_stack_call_func (thread_join_callback, tstate);
400
401 return Qnil;
402}
403
404DEFUN ("all-threads", Fall_threads, Sall_threads, 0, 0, 0,
405 doc: /* Return a list of all threads. */)
406 (void)
407{
408 Lisp_Object result = Qnil;
409 struct thread_state *iter;
410
411 for (iter = all_threads; iter; iter = iter->next_thread)
412 {
413 Lisp_Object thread;
414
415 XSETTHREAD (thread, iter);
416 result = Fcons (thread, result);
417 }
418
419 return result;
420}
421
422
423
424static void
425init_primary_thread (void)
426{
427 primary_thread.header.size
120 = PSEUDOVECSIZE (struct thread_state, m_gcprolist); 428 = PSEUDOVECSIZE (struct thread_state, m_gcprolist);
121 XSETPVECTYPE (&the_only_thread, PVEC_THREAD); 429 XSETPVECTYPE (&primary_thread, PVEC_THREAD);
122 the_only_thread.m_last_thing_searched = Qnil; 430 primary_thread.m_last_thing_searched = Qnil;
123 the_only_thread.m_saved_last_thing_searched = Qnil; 431 primary_thread.m_saved_last_thing_searched = Qnil;
432 primary_thread.name = Qnil;
433 primary_thread.function = Qnil;
434 primary_thread.error_symbol = Qnil;
435 primary_thread.error_data = Qnil;
436
437 sys_cond_init (&primary_thread.thread_condvar);
438}
439
440void
441init_threads_once (void)
442{
443 init_primary_thread ();
124} 444}
125 445
126void 446void
127init_threads (void) 447init_threads (void)
128{ 448{
449 init_primary_thread ();
450
129 sys_mutex_init (&global_lock); 451 sys_mutex_init (&global_lock);
130 sys_mutex_lock (&global_lock); 452 sys_mutex_lock (&global_lock);
131} 453}
454
455void
456syms_of_threads (void)
457{
458 defsubr (&Sthread_yield);
459 defsubr (&Smake_thread);
460 defsubr (&Scurrent_thread);
461 defsubr (&Sthread_name);
462 defsubr (&Sthread_signal);
463 defsubr (&Sthread_alive_p);
464 defsubr (&Sthread_join);
465 defsubr (&Sall_threads);
466
467 Qthreadp = intern_c_string ("threadp");
468 staticpro (&Qthreadp);
469}