(scm_try_mutex): Renamed argument for consistency.
[bpt/guile.git] / libguile / threads.c
1 /* Copyright (C) 1995,1996,1997,1998,2000,2001, 2002, 2003, 2004 Free Software Foundation, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18
19 \f
20
21 #define _GNU_SOURCE
22
23 #include "libguile/_scm.h"
24
25 #if HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #include <stdio.h>
29 #include <assert.h>
30 #if HAVE_SYS_TIME_H
31 #include <sys/time.h>
32 #endif
33
34 #include "libguile/validate.h"
35 #include "libguile/root.h"
36 #include "libguile/eval.h"
37 #include "libguile/async.h"
38 #include "libguile/ports.h"
39 #include "libguile/threads.h"
40 #include "libguile/dynwind.h"
41 #include "libguile/iselect.h"
42 #include "libguile/fluids.h"
43 #include "libguile/continuations.h"
44 #include "libguile/init.h"
45
46 /*** Queues */
47
48 /* Make an empty queue data structure.
49 */
50 static SCM
51 make_queue ()
52 {
53 return scm_cons (SCM_EOL, SCM_EOL);
54 }
55
56 /* Put T at the back of Q and return a handle that can be used with
57 remqueue to remove T from Q again.
58 */
59 static SCM
60 enqueue (SCM q, SCM t)
61 {
62 SCM c = scm_cons (t, SCM_EOL);
63 if (scm_is_null (SCM_CDR (q)))
64 SCM_SETCDR (q, c);
65 else
66 SCM_SETCDR (SCM_CAR (q), c);
67 SCM_SETCAR (q, c);
68 return c;
69 }
70
71 /* Remove the element that the handle C refers to from the queue Q. C
72 must have been returned from a call to enqueue. The return value
73 is zero when the element referred to by C has already been removed.
74 Otherwise, 1 is returned.
75 */
76 static int
77 remqueue (SCM q, SCM c)
78 {
79 SCM p, prev = q;
80 for (p = SCM_CDR (q); !scm_is_null (p); p = SCM_CDR (p))
81 {
82 if (scm_is_eq (p, c))
83 {
84 if (scm_is_eq (c, SCM_CAR (q)))
85 SCM_SETCAR (q, SCM_CDR (c));
86 SCM_SETCDR (prev, SCM_CDR (c));
87 return 1;
88 }
89 prev = p;
90 }
91 return 0;
92 }
93
94 /* Remove the front-most element from the queue Q and return it.
95 Return SCM_BOOL_F when Q is empty.
96 */
97 static SCM
98 dequeue (SCM q)
99 {
100 SCM c = SCM_CDR (q);
101 if (scm_is_null (c))
102 return SCM_BOOL_F;
103 else
104 {
105 SCM_SETCDR (q, SCM_CDR (c));
106 if (scm_is_null (SCM_CDR (q)))
107 SCM_SETCAR (q, SCM_EOL);
108 return SCM_CAR (c);
109 }
110 }
111
112 /*** Thread smob routines */
113
114 static SCM
115 thread_mark (SCM obj)
116 {
117 scm_i_thread *t = SCM_I_THREAD_DATA (obj);
118 scm_gc_mark (t->result);
119 scm_gc_mark (t->join_queue);
120 scm_gc_mark (t->dynwinds);
121 scm_gc_mark (t->active_asyncs);
122 scm_gc_mark (t->signal_asyncs);
123 scm_gc_mark (t->continuation_root);
124 return t->dynamic_state;
125 }
126
127 static int
128 thread_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
129 {
130 scm_i_thread *t = SCM_I_THREAD_DATA (exp);
131 scm_puts ("#<thread ", port);
132 scm_uintprint ((size_t)t->pthread, 10, port);
133 scm_puts (" (", port);
134 scm_uintprint ((scm_t_bits)t, 16, port);
135 scm_puts (")>", port);
136 return 1;
137 }
138
139 static size_t
140 thread_free (SCM obj)
141 {
142 scm_i_thread *t = SCM_I_THREAD_DATA (obj);
143 assert (t->exited);
144 scm_gc_free (t, sizeof (*t), "thread");
145 return 0;
146 }
147
148 /*** Blocking on queues. */
149
150 /* See also scm_i_queue_async_cell for how such a block is
151 interrputed.
152 */
153
154 /* Put the current thread on QUEUE and go to sleep, waiting for it to
155 be woken up by a call to 'unblock_from_queue', or to be
156 interrupted. Upon return of this function, the current thread is
157 no longer on QUEUE, even when the sleep has been interrupted.
158
159 The QUEUE data structure is assumed to be protected by MUTEX and
160 the caller of block_self must hold MUTEX. It will be atomically
161 unlocked while sleeping, just as with scm_i_pthread_cond_wait.
162
163 SLEEP_OBJECT is an arbitrary SCM value that is kept alive as long
164 as MUTEX is needed.
165
166 When WAITTIME is not NULL, the sleep will be aborted at that time.
167
168 The return value of block_self is an errno value. It will be zero
169 when the sleep has been successfully completed by a call to
170 unblock_from_queue, EINTR when it has been interrupted by the
171 delivery of a system async, and ETIMEDOUT when the timeout has
172 expired.
173
174 The system asyncs themselves are not executed by block_self.
175 */
176 static int
177 block_self (SCM queue, SCM sleep_object, scm_i_pthread_mutex_t *mutex,
178 const scm_t_timespec *waittime)
179 {
180 scm_i_thread *t = SCM_I_CURRENT_THREAD;
181 SCM q_handle;
182 int err;
183
184 if (scm_i_setup_sleep (t, sleep_object, mutex, -1))
185 err = EINTR;
186 else
187 {
188 t->block_asyncs++;
189 q_handle = enqueue (queue, t->handle);
190 if (waittime == NULL)
191 err = scm_i_scm_pthread_cond_wait (&t->sleep_cond, mutex);
192 else
193 err = scm_i_scm_pthread_cond_timedwait (&t->sleep_cond, mutex, waittime);
194
195 /* When we are still on QUEUE, we have been interrupted. We
196 report this only when no other error (such as a timeout) has
197 happened above.
198 */
199 if (remqueue (queue, q_handle) && err == 0)
200 err = EINTR;
201 t->block_asyncs--;
202 scm_i_reset_sleep (t);
203 }
204
205 return err;
206 }
207
208 /* Wake up the first thread on QUEUE, if any. The caller must hold
209 the mutex that protects QUEUE. The awoken thread is returned, or
210 #f when the queue was empty.
211 */
212 static SCM
213 unblock_from_queue (SCM queue)
214 {
215 SCM thread = dequeue (queue);
216 if (scm_is_true (thread))
217 scm_i_pthread_cond_signal (&SCM_I_THREAD_DATA(thread)->sleep_cond);
218 return thread;
219 }
220
221 /* Getting into and out of guile mode.
222 */
223
224 scm_i_pthread_key_t scm_i_thread_key;
225
226 static void
227 resume (scm_i_thread *t)
228 {
229 t->top = NULL;
230 if (t->clear_freelists_p)
231 {
232 *SCM_FREELIST_LOC (scm_i_freelist) = SCM_EOL;
233 *SCM_FREELIST_LOC (scm_i_freelist2) = SCM_EOL;
234 t->clear_freelists_p = 0;
235 }
236 }
237
238 void
239 scm_enter_guile (scm_t_guile_ticket ticket)
240 {
241 scm_i_thread *t = (scm_i_thread *)ticket;
242 if (t)
243 {
244 scm_i_pthread_mutex_lock (&t->heap_mutex);
245 resume (t);
246 }
247 }
248
249 static scm_i_thread *
250 suspend (void)
251 {
252 scm_i_thread *t = SCM_I_CURRENT_THREAD;
253
254 /* record top of stack for the GC */
255 t->top = SCM_STACK_PTR (&t);
256 /* save registers. */
257 SCM_FLUSH_REGISTER_WINDOWS;
258 setjmp (t->regs);
259 return t;
260 }
261
262 scm_t_guile_ticket
263 scm_leave_guile ()
264 {
265 scm_i_thread *t = suspend ();
266 scm_i_pthread_mutex_unlock (&t->heap_mutex);
267 return (scm_t_guile_ticket) t;
268 }
269
270 static scm_i_pthread_mutex_t thread_admin_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
271 static scm_i_thread *all_threads = NULL;
272 static int thread_count;
273
274 static SCM scm_i_default_dynamic_state;
275
276 /* Perform first stage of thread initialisation, in non-guile mode.
277 */
278 static void
279 guilify_self_1 (SCM_STACKITEM *base)
280 {
281 scm_i_thread *t = malloc (sizeof (scm_i_thread));
282
283 t->pthread = scm_i_pthread_self ();
284 t->handle = SCM_BOOL_F;
285 t->result = SCM_BOOL_F;
286 t->join_queue = SCM_EOL;
287 t->dynamic_state = SCM_BOOL_F;
288 t->dynwinds = SCM_EOL;
289 t->active_asyncs = SCM_EOL;
290 t->signal_asyncs = SCM_EOL;
291 t->block_asyncs = 1;
292 t->pending_asyncs = 1;
293 t->last_debug_frame = NULL;
294 t->base = base;
295 t->continuation_base = base;
296 scm_i_pthread_cond_init (&t->sleep_cond, NULL);
297 t->sleep_mutex = NULL;
298 t->sleep_object = SCM_BOOL_F;
299 t->sleep_fd = -1;
300 pipe (t->sleep_pipe);
301 scm_i_pthread_mutex_init (&t->heap_mutex, NULL);
302 t->clear_freelists_p = 0;
303 t->exited = 0;
304
305 t->freelist = SCM_EOL;
306 t->freelist2 = SCM_EOL;
307 SCM_SET_FREELIST_LOC (scm_i_freelist, &t->freelist);
308 SCM_SET_FREELIST_LOC (scm_i_freelist2, &t->freelist2);
309
310 scm_i_pthread_setspecific (scm_i_thread_key, t);
311
312 scm_i_pthread_mutex_lock (&t->heap_mutex);
313
314 scm_i_pthread_mutex_lock (&thread_admin_mutex);
315 t->next_thread = all_threads;
316 all_threads = t;
317 thread_count++;
318 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
319 }
320
321 /* Perform second stage of thread initialisation, in guile mode.
322 */
323 static void
324 guilify_self_2 (SCM parent)
325 {
326 scm_i_thread *t = SCM_I_CURRENT_THREAD;
327
328 SCM_NEWSMOB (t->handle, scm_tc16_thread, t);
329 scm_gc_register_collectable_memory (t, sizeof (scm_i_thread), "thread");
330 t->continuation_root = scm_cons (t->handle, SCM_EOL);
331 t->continuation_base = t->base;
332
333 if (scm_is_true (parent))
334 t->dynamic_state = scm_make_dynamic_state (parent);
335 else
336 t->dynamic_state = scm_i_make_initial_dynamic_state ();
337
338 t->join_queue = make_queue ();
339 t->block_asyncs = 0;
340 }
341
342 /* Perform thread tear-down, in guile mode.
343 */
344 static void *
345 do_thread_exit (void *v)
346 {
347 scm_i_thread *t = (scm_i_thread *)v, **tp;
348
349 scm_i_scm_pthread_mutex_lock (&thread_admin_mutex);
350
351 t->exited = 1;
352 while (scm_is_true (unblock_from_queue (t->join_queue)))
353 ;
354
355 for (tp = &all_threads; *tp; tp = &(*tp)->next_thread)
356 if (*tp == t)
357 {
358 *tp = t->next_thread;
359 break;
360 }
361 thread_count--;
362
363 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
364 return NULL;
365 }
366
367 static void
368 on_thread_exit (void *v)
369 {
370 scm_i_pthread_setspecific (scm_i_thread_key, v);
371 scm_with_guile (do_thread_exit, v);
372 scm_i_pthread_setspecific (scm_i_thread_key, NULL);
373 }
374
375 static scm_i_pthread_once_t init_thread_key_once = SCM_I_PTHREAD_ONCE_INIT;
376
377 static void
378 init_thread_key (void)
379 {
380 scm_i_pthread_key_create (&scm_i_thread_key, on_thread_exit);
381 }
382
383 /* Perform any initializations necessary to bring the current thread
384 into guile mode, initializing Guile itself, if necessary.
385
386 BASE is the stack base to use with GC.
387
388 PARENT is the dynamic state to use as the parent, ot SCM_BOOL_F in
389 which case the default dynamic state is used.
390
391 Return zero when the thread was in guile mode already; otherwise
392 return 1.
393 */
394
395 static int
396 scm_i_init_thread_for_guile (SCM_STACKITEM *base, SCM parent)
397 {
398 scm_i_thread *t;
399
400 scm_i_pthread_once (&init_thread_key_once, init_thread_key);
401
402 if ((t = SCM_I_CURRENT_THREAD) == NULL)
403 {
404 /* This thread has not been guilified yet.
405 */
406
407 scm_i_pthread_mutex_lock (&scm_i_init_mutex);
408 if (scm_initialized_p == 0)
409 {
410 /* First thread ever to enter Guile. Run the full
411 initialization.
412 */
413 scm_i_init_guile (base);
414 scm_i_pthread_mutex_unlock (&scm_i_init_mutex);
415 }
416 else
417 {
418 /* Guile is already initialized, but this thread enters it for
419 the first time. Only initialize this thread.
420 */
421 scm_i_pthread_mutex_unlock (&scm_i_init_mutex);
422 guilify_self_1 (base);
423 guilify_self_2 (parent);
424 }
425 return 1;
426 }
427 else if (t->top)
428 {
429 /* This thread is already guilified but not in guile mode, just
430 resume it.
431
432 XXX - base might be lower than when this thread was first
433 guilified.
434 */
435 scm_enter_guile ((scm_t_guile_ticket) t);
436 return 1;
437 }
438 else
439 {
440 /* Thread is already in guile mode. Nothing to do.
441 */
442 return 0;
443 }
444 }
445
446 #ifdef HAVE_LIBC_STACK_END
447
448 extern void *__libc_stack_end;
449
450 #if SCM_USE_PTHREAD_THREADS
451 #ifdef HAVE_PTHREAD_ATTR_GETSTACK
452
453 #define HAVE_GET_THREAD_STACK_BASE
454
455 static SCM_STACKITEM *
456 get_thread_stack_base ()
457 {
458 pthread_attr_t attr;
459 void *start, *end;
460 size_t size;
461
462 /* XXX - pthread_getattr_np from LinuxThreads does not seem to work
463 for the main thread, but we can use __libc_stack_end in that
464 case.
465 */
466
467 pthread_getattr_np (pthread_self (), &attr);
468 pthread_attr_getstack (&attr, &start, &size);
469 end = (char *)start + size;
470
471 if ((void *)&attr < start || (void *)&attr >= end)
472 return __libc_stack_end;
473 else
474 {
475 #if SCM_STACK_GROWS_UP
476 return start;
477 #else
478 return end;
479 #endif
480 }
481 }
482
483 #endif /* HAVE_PTHREAD_ATTR_GETSTACK */
484
485 #else /* !SCM_USE_PTHREAD_THREADS */
486
487 #define HAVE_GET_THREAD_STACK_BASE
488
489 static SCM_STACKITEM *
490 get_thread_stack_base ()
491 {
492 return __libc_stack_end;
493 }
494
495 #endif /* !SCM_USE_PTHREAD_THREADS */
496 #endif /* HAVE_LIBC_STACK_END */
497
498 #ifdef HAVE_GET_THREAD_STACK_BASE
499
500 void
501 scm_init_guile ()
502 {
503 scm_i_init_thread_for_guile (get_thread_stack_base (),
504 scm_i_default_dynamic_state);
505 }
506
507 #endif
508
509 void *
510 scm_with_guile (void *(*func)(void *), void *data)
511 {
512 return scm_i_with_guile_and_parent (func, data,
513 scm_i_default_dynamic_state);
514 }
515
516 void *
517 scm_i_with_guile_and_parent (void *(*func)(void *), void *data,
518 SCM parent)
519 {
520 void *res;
521 int really_entered;
522 SCM_STACKITEM base_item;
523 really_entered = scm_i_init_thread_for_guile (&base_item, parent);
524 res = scm_c_with_continuation_barrier (func, data);
525 if (really_entered)
526 scm_leave_guile ();
527 return res;
528 }
529
530 void *
531 scm_without_guile (void *(*func)(void *), void *data)
532 {
533 void *res;
534 scm_t_guile_ticket t;
535 t = scm_leave_guile ();
536 res = func (data);
537 scm_enter_guile (t);
538 return res;
539 }
540
541 /*** Thread creation */
542
543 typedef struct {
544 SCM parent;
545 SCM thunk;
546 SCM handler;
547 SCM thread;
548 scm_i_pthread_mutex_t mutex;
549 scm_i_pthread_cond_t cond;
550 } launch_data;
551
552 static void *
553 really_launch (void *d)
554 {
555 launch_data *data = (launch_data *)d;
556 SCM thunk = data->thunk, handler = data->handler;
557 scm_i_thread *t;
558
559 t = SCM_I_CURRENT_THREAD;
560
561 scm_i_scm_pthread_mutex_lock (&data->mutex);
562 data->thread = scm_current_thread ();
563 scm_i_pthread_cond_signal (&data->cond);
564 scm_i_pthread_mutex_unlock (&data->mutex);
565
566 if (SCM_UNBNDP (handler))
567 t->result = scm_call_0 (thunk);
568 else
569 t->result = scm_catch (SCM_BOOL_T, thunk, handler);
570
571 return 0;
572 }
573
574 static void *
575 launch_thread (void *d)
576 {
577 launch_data *data = (launch_data *)d;
578 scm_i_pthread_detach (scm_i_pthread_self ());
579 scm_i_with_guile_and_parent (really_launch, d, data->parent);
580 return NULL;
581 }
582
583 SCM_DEFINE (scm_call_with_new_thread, "call-with-new-thread", 1, 1, 0,
584 (SCM thunk, SCM handler),
585 "Call @code{thunk} in a new thread and with a new dynamic state,\n"
586 "returning a new thread object representing the thread. The procedure\n"
587 "@var{thunk} is called via @code{with-continuation-barrier}.\n"
588 "\n"
589 "When @var{handler} is specified, then @var{thunk} is called from\n"
590 "within a @code{catch} with tag @code{#t} that has @var{handler} as its\n"
591 "handler. This catch is established inside the continuation barrier.\n"
592 "\n"
593 "Once @var{thunk} or @var{handler} returns, the return value is made\n"
594 "the @emph{exit value} of the thread and the thread is terminated.")
595 #define FUNC_NAME s_scm_call_with_new_thread
596 {
597 launch_data data;
598 scm_i_pthread_t id;
599 int err;
600
601 SCM_ASSERT (scm_is_true (scm_thunk_p (thunk)), thunk, SCM_ARG1, FUNC_NAME);
602 SCM_ASSERT (SCM_UNBNDP (handler) || scm_is_true (scm_procedure_p (handler)),
603 handler, SCM_ARG2, FUNC_NAME);
604
605 data.parent = scm_current_dynamic_state ();
606 data.thunk = thunk;
607 data.handler = handler;
608 data.thread = SCM_BOOL_F;
609 scm_i_pthread_mutex_init (&data.mutex, NULL);
610 scm_i_pthread_cond_init (&data.cond, NULL);
611
612 scm_i_scm_pthread_mutex_lock (&data.mutex);
613 err = scm_i_pthread_create (&id, NULL, launch_thread, &data);
614 if (err)
615 {
616 scm_i_pthread_mutex_unlock (&data.mutex);
617 errno = err;
618 scm_syserror (NULL);
619 }
620 scm_i_scm_pthread_cond_wait (&data.cond, &data.mutex);
621 scm_i_pthread_mutex_unlock (&data.mutex);
622
623 return data.thread;
624 }
625 #undef FUNC_NAME
626
627 typedef struct {
628 SCM parent;
629 scm_t_catch_body body;
630 void *body_data;
631 scm_t_catch_handler handler;
632 void *handler_data;
633 SCM thread;
634 scm_i_pthread_mutex_t mutex;
635 scm_i_pthread_cond_t cond;
636 } spawn_data;
637
638 static void *
639 really_spawn (void *d)
640 {
641 spawn_data *data = (spawn_data *)d;
642 scm_t_catch_body body = data->body;
643 void *body_data = data->body_data;
644 scm_t_catch_handler handler = data->handler;
645 void *handler_data = data->handler_data;
646 scm_i_thread *t = SCM_I_CURRENT_THREAD;
647
648 scm_i_scm_pthread_mutex_lock (&data->mutex);
649 data->thread = scm_current_thread ();
650 scm_i_pthread_cond_signal (&data->cond);
651 scm_i_pthread_mutex_unlock (&data->mutex);
652
653 if (handler == NULL)
654 t->result = body (body_data);
655 else
656 t->result = scm_internal_catch (SCM_BOOL_T,
657 body, body_data,
658 handler, handler_data);
659
660 return 0;
661 }
662
663 static void *
664 spawn_thread (void *d)
665 {
666 spawn_data *data = (spawn_data *)d;
667 scm_i_pthread_detach (scm_i_pthread_self ());
668 scm_i_with_guile_and_parent (really_spawn, d, data->parent);
669 return NULL;
670 }
671
672 SCM
673 scm_spawn_thread (scm_t_catch_body body, void *body_data,
674 scm_t_catch_handler handler, void *handler_data)
675 {
676 spawn_data data;
677 scm_i_pthread_t id;
678 int err;
679
680 data.parent = scm_current_dynamic_state ();
681 data.body = body;
682 data.body_data = body_data;
683 data.handler = handler;
684 data.handler_data = handler_data;
685 data.thread = SCM_BOOL_F;
686 scm_i_pthread_mutex_init (&data.mutex, NULL);
687 scm_i_pthread_cond_init (&data.cond, NULL);
688
689 scm_i_scm_pthread_mutex_lock (&data.mutex);
690 err = scm_i_pthread_create (&id, NULL, spawn_thread, &data);
691 if (err)
692 {
693 scm_i_pthread_mutex_unlock (&data.mutex);
694 errno = err;
695 scm_syserror (NULL);
696 }
697 scm_i_scm_pthread_cond_wait (&data.cond, &data.mutex);
698 scm_i_pthread_mutex_unlock (&data.mutex);
699
700 return data.thread;
701 }
702
703 SCM_DEFINE (scm_yield, "yield", 0, 0, 0,
704 (),
705 "Move the calling thread to the end of the scheduling queue.")
706 #define FUNC_NAME s_scm_yield
707 {
708 return scm_from_bool (scm_i_sched_yield ());
709 }
710 #undef FUNC_NAME
711
712 SCM_DEFINE (scm_join_thread, "join-thread", 1, 0, 0,
713 (SCM thread),
714 "Suspend execution of the calling thread until the target @var{thread} "
715 "terminates, unless the target @var{thread} has already terminated. ")
716 #define FUNC_NAME s_scm_join_thread
717 {
718 scm_i_thread *t;
719 SCM res;
720
721 SCM_VALIDATE_THREAD (1, thread);
722 if (scm_is_eq (scm_current_thread (), thread))
723 SCM_MISC_ERROR ("can not join the current thread", SCM_EOL);
724
725 scm_i_scm_pthread_mutex_lock (&thread_admin_mutex);
726
727 t = SCM_I_THREAD_DATA (thread);
728 if (!t->exited)
729 {
730 while (1)
731 {
732 block_self (t->join_queue, thread, &thread_admin_mutex, NULL);
733 if (t->exited)
734 break;
735 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
736 SCM_TICK;
737 scm_i_scm_pthread_mutex_lock (&thread_admin_mutex);
738 }
739 }
740 res = t->result;
741
742 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
743 return res;
744 }
745 #undef FUNC_NAME
746
747 /*** Fat mutexes */
748
749 /* We implement our own mutex type since we want them to be 'fair', we
750 want to do fancy things while waiting for them (like running
751 asyncs) and we might want to add things that are nice for
752 debugging.
753 */
754
755 typedef struct {
756 scm_i_pthread_mutex_t lock;
757 SCM owner;
758 int level; /* how much the owner owns us.
759 < 0 for non-recursive mutexes */
760 SCM waiting; /* the threads waiting for this mutex. */
761 } fat_mutex;
762
763 #define SCM_MUTEXP(x) SCM_SMOB_PREDICATE (scm_tc16_mutex, x)
764 #define SCM_MUTEX_DATA(x) ((fat_mutex *) SCM_SMOB_DATA (x))
765
766 static SCM
767 fat_mutex_mark (SCM mx)
768 {
769 fat_mutex *m = SCM_MUTEX_DATA (mx);
770 scm_gc_mark (m->owner);
771 return m->waiting;
772 }
773
774 static size_t
775 fat_mutex_free (SCM mx)
776 {
777 fat_mutex *m = SCM_MUTEX_DATA (mx);
778 scm_i_pthread_mutex_destroy (&m->lock);
779 scm_gc_free (m, sizeof (fat_mutex), "mutex");
780 return 0;
781 }
782
783 static int
784 fat_mutex_print (SCM mx, SCM port, scm_print_state *pstate SCM_UNUSED)
785 {
786 fat_mutex *m = SCM_MUTEX_DATA (mx);
787 scm_puts ("#<mutex ", port);
788 scm_uintprint ((scm_t_bits)m, 16, port);
789 scm_puts (">", port);
790 return 1;
791 }
792
793 static SCM
794 make_fat_mutex (int recursive)
795 {
796 fat_mutex *m;
797 SCM mx;
798
799 m = scm_gc_malloc (sizeof (fat_mutex), "mutex");
800 scm_i_pthread_mutex_init (&m->lock, NULL);
801 m->owner = SCM_BOOL_F;
802 m->level = recursive? 0 : -1;
803 m->waiting = SCM_EOL;
804 SCM_NEWSMOB (mx, scm_tc16_mutex, (scm_t_bits) m);
805 m->waiting = make_queue ();
806 return mx;
807 }
808
809 SCM_DEFINE (scm_make_mutex, "make-mutex", 0, 0, 0,
810 (void),
811 "Create a new mutex. ")
812 #define FUNC_NAME s_scm_make_mutex
813 {
814 return make_fat_mutex (0);
815 }
816 #undef FUNC_NAME
817
818 SCM_DEFINE (scm_make_recursive_mutex, "make-recursive-mutex", 0, 0, 0,
819 (void),
820 "Create a new recursive mutex. ")
821 #define FUNC_NAME s_scm_make_recursive_mutex
822 {
823 return make_fat_mutex (1);
824 }
825 #undef FUNC_NAME
826
827 static char *
828 fat_mutex_lock (SCM mutex)
829 {
830 fat_mutex *m = SCM_MUTEX_DATA (mutex);
831 SCM thread = scm_current_thread ();
832 char *msg = NULL;
833
834 scm_i_scm_pthread_mutex_lock (&m->lock);
835 if (scm_is_false (m->owner))
836 m->owner = thread;
837 else if (scm_is_eq (m->owner, thread))
838 {
839 if (m->level >= 0)
840 m->level++;
841 else
842 msg = "mutex already locked by current thread";
843 }
844 else
845 {
846 while (1)
847 {
848 block_self (m->waiting, mutex, &m->lock, NULL);
849 if (scm_is_eq (m->owner, thread))
850 break;
851 scm_i_pthread_mutex_unlock (&m->lock);
852 SCM_TICK;
853 scm_i_scm_pthread_mutex_lock (&m->lock);
854 }
855 }
856 scm_i_pthread_mutex_unlock (&m->lock);
857 return msg;
858 }
859
860 SCM_DEFINE (scm_lock_mutex, "lock-mutex", 1, 0, 0,
861 (SCM mx),
862 "Lock @var{mutex}. If the mutex is already locked, the calling thread "
863 "blocks until the mutex becomes available. The function returns when "
864 "the calling thread owns the lock on @var{mutex}. Locking a mutex that "
865 "a thread already owns will succeed right away and will not block the "
866 "thread. That is, Guile's mutexes are @emph{recursive}. ")
867 #define FUNC_NAME s_scm_lock_mutex
868 {
869 SCM_VALIDATE_MUTEX (1, mx);
870 char *msg;
871
872 msg = fat_mutex_lock (mx);
873 if (msg)
874 scm_misc_error (NULL, msg, SCM_EOL);
875 return SCM_BOOL_T;
876 }
877 #undef FUNC_NAME
878
879 static char *
880 fat_mutex_trylock (fat_mutex *m, int *resp)
881 {
882 char *msg = NULL;
883 SCM thread = scm_current_thread ();
884
885 *resp = 1;
886 scm_i_pthread_mutex_lock (&m->lock);
887 if (scm_is_false (m->owner))
888 m->owner = thread;
889 else if (scm_is_eq (m->owner, thread))
890 {
891 if (m->level >= 0)
892 m->level++;
893 else
894 msg = "mutex already locked by current thread";
895 }
896 else
897 *resp = 0;
898 scm_i_pthread_mutex_unlock (&m->lock);
899 return msg;
900 }
901
902 SCM_DEFINE (scm_try_mutex, "try-mutex", 1, 0, 0,
903 (SCM mutex),
904 "Try to lock @var{mutex}. If the mutex is already locked by someone "
905 "else, return @code{#f}. Else lock the mutex and return @code{#t}. ")
906 #define FUNC_NAME s_scm_try_mutex
907 {
908 char *msg;
909 int res;
910
911 SCM_VALIDATE_MUTEX (1, mutex);
912
913 msg = fat_mutex_trylock (SCM_MUTEX_DATA (mutex), &res);
914 if (msg)
915 scm_misc_error (NULL, msg, SCM_EOL);
916 return scm_from_bool (res);
917 }
918 #undef FUNC_NAME
919
920 static char *
921 fat_mutex_unlock (fat_mutex *m)
922 {
923 char *msg = NULL;
924
925 scm_i_scm_pthread_mutex_lock (&m->lock);
926 if (!scm_is_eq (m->owner, scm_current_thread ()))
927 {
928 if (scm_is_false (m->owner))
929 msg = "mutex not locked";
930 else
931 msg = "mutex not locked by current thread";
932 }
933 else if (m->level > 0)
934 m->level--;
935 else
936 m->owner = unblock_from_queue (m->waiting);
937 scm_i_pthread_mutex_unlock (&m->lock);
938
939 return msg;
940 }
941
942 SCM_DEFINE (scm_unlock_mutex, "unlock-mutex", 1, 0, 0,
943 (SCM mx),
944 "Unlocks @var{mutex} if the calling thread owns the lock on "
945 "@var{mutex}. Calling unlock-mutex on a mutex not owned by the current "
946 "thread results in undefined behaviour. Once a mutex has been unlocked, "
947 "one thread blocked on @var{mutex} is awakened and grabs the mutex "
948 "lock. Every call to @code{lock-mutex} by this thread must be matched "
949 "with a call to @code{unlock-mutex}. Only the last call to "
950 "@code{unlock-mutex} will actually unlock the mutex. ")
951 #define FUNC_NAME s_scm_unlock_mutex
952 {
953 char *msg;
954 SCM_VALIDATE_MUTEX (1, mx);
955
956 msg = fat_mutex_unlock (SCM_MUTEX_DATA (mx));
957 if (msg)
958 scm_misc_error (NULL, msg, SCM_EOL);
959 return SCM_BOOL_T;
960 }
961 #undef FUNC_NAME
962
963 #if 0
964
965 SCM_DEFINE (scm_mutex_owner, "mutex-owner", 1, 0, 0,
966 (SCM mx),
967 "Return the thread owning @var{mx}, or @code{#f}.")
968 #define FUNC_NAME s_scm_mutex_owner
969 {
970 SCM_VALIDATE_MUTEX (1, mx);
971 return (SCM_MUTEX_DATA(mx))->owner;
972 }
973 #undef FUNC_NAME
974
975 SCM_DEFINE (scm_mutex_level, "mutex-level", 1, 0, 0,
976 (SCM mx),
977 "Return the lock level of a recursive mutex, or -1\n"
978 "for a standard mutex.")
979 #define FUNC_NAME s_scm_mutex_level
980 {
981 SCM_VALIDATE_MUTEX (1, mx);
982 return scm_from_int (SCM_MUTEX_DATA(mx)->level);
983 }
984 #undef FUNC_NAME
985
986 #endif
987
988 /*** Fat condition variables */
989
990 typedef struct {
991 scm_i_pthread_mutex_t lock;
992 SCM waiting; /* the threads waiting for this condition. */
993 } fat_cond;
994
995 #define SCM_CONDVARP(x) SCM_SMOB_PREDICATE (scm_tc16_condvar, x)
996 #define SCM_CONDVAR_DATA(x) ((fat_cond *) SCM_SMOB_DATA (x))
997
998 static SCM
999 fat_cond_mark (SCM cv)
1000 {
1001 fat_cond *c = SCM_CONDVAR_DATA (cv);
1002 return c->waiting;
1003 }
1004
1005 static size_t
1006 fat_cond_free (SCM mx)
1007 {
1008 fat_cond *c = SCM_CONDVAR_DATA (mx);
1009 scm_i_pthread_mutex_destroy (&c->lock);
1010 scm_gc_free (c, sizeof (fat_cond), "condition-variable");
1011 return 0;
1012 }
1013
1014 static int
1015 fat_cond_print (SCM cv, SCM port, scm_print_state *pstate SCM_UNUSED)
1016 {
1017 fat_cond *c = SCM_CONDVAR_DATA (cv);
1018 scm_puts ("#<condition-variable ", port);
1019 scm_uintprint ((scm_t_bits)c, 16, port);
1020 scm_puts (">", port);
1021 return 1;
1022 }
1023
1024 SCM_DEFINE (scm_make_condition_variable, "make-condition-variable", 0, 0, 0,
1025 (void),
1026 "Make a new condition variable.")
1027 #define FUNC_NAME s_scm_make_condition_variable
1028 {
1029 fat_cond *c;
1030 SCM cv;
1031
1032 c = scm_gc_malloc (sizeof (fat_cond), "condition variable");
1033 scm_i_pthread_mutex_init (&c->lock, 0);
1034 c->waiting = SCM_EOL;
1035 SCM_NEWSMOB (cv, scm_tc16_condvar, (scm_t_bits) c);
1036 c->waiting = make_queue ();
1037 return cv;
1038 }
1039 #undef FUNC_NAME
1040
1041 static int
1042 fat_cond_timedwait (SCM cond, SCM mutex,
1043 const scm_t_timespec *waittime)
1044 {
1045 scm_i_thread *t = SCM_I_CURRENT_THREAD;
1046 fat_cond *c = SCM_CONDVAR_DATA (cond);
1047 fat_mutex *m = SCM_MUTEX_DATA (mutex);
1048 const char *msg;
1049 int err = 0;
1050
1051 while (1)
1052 {
1053 fprintf (stderr, "cond wait on %p\n", &c->lock);
1054
1055 scm_i_scm_pthread_mutex_lock (&c->lock);
1056 msg = fat_mutex_unlock (m);
1057 t->block_asyncs++;
1058 if (msg == NULL)
1059 {
1060 err = block_self (c->waiting, cond, &c->lock, waittime);
1061 scm_i_pthread_mutex_unlock (&c->lock);
1062 fprintf (stderr, "locking mutex\n");
1063 fat_mutex_lock (mutex);
1064 }
1065 else
1066 scm_i_pthread_mutex_unlock (&c->lock);
1067 t->block_asyncs--;
1068 scm_async_click ();
1069
1070 fprintf (stderr, "back: %s, %d\n", msg, err);
1071
1072 if (msg)
1073 scm_misc_error (NULL, msg, SCM_EOL);
1074
1075 scm_remember_upto_here_2 (cond, mutex);
1076
1077 if (err == 0)
1078 return 1;
1079 if (err == ETIMEDOUT)
1080 return 0;
1081 if (err != EINTR)
1082 {
1083 errno = err;
1084 scm_syserror (NULL);
1085 }
1086 }
1087 }
1088
1089 SCM_DEFINE (scm_timed_wait_condition_variable, "wait-condition-variable", 2, 1, 0,
1090 (SCM cv, SCM mx, SCM t),
1091 "Wait until @var{cond-var} has been signalled. While waiting, "
1092 "@var{mutex} is atomically unlocked (as with @code{unlock-mutex}) and "
1093 "is locked again when this function returns. When @var{time} is given, "
1094 "it specifies a point in time where the waiting should be aborted. It "
1095 "can be either a integer as returned by @code{current-time} or a pair "
1096 "as returned by @code{gettimeofday}. When the waiting is aborted the "
1097 "mutex is locked and @code{#f} is returned. When the condition "
1098 "variable is in fact signalled, the mutex is also locked and @code{#t} "
1099 "is returned. ")
1100 #define FUNC_NAME s_scm_timed_wait_condition_variable
1101 {
1102 scm_t_timespec waittime, *waitptr = NULL;
1103
1104 SCM_VALIDATE_CONDVAR (1, cv);
1105 SCM_VALIDATE_MUTEX (2, mx);
1106
1107 if (!SCM_UNBNDP (t))
1108 {
1109 if (scm_is_pair (t))
1110 {
1111 waittime.tv_sec = scm_to_ulong (SCM_CAR (t));
1112 waittime.tv_nsec = scm_to_ulong (SCM_CAR (t)) * 1000;
1113 }
1114 else
1115 {
1116 waittime.tv_sec = scm_to_ulong (t);
1117 waittime.tv_nsec = 0;
1118 }
1119 waitptr = &waittime;
1120 }
1121
1122 return scm_from_bool (fat_cond_timedwait (cv, mx, waitptr));
1123 }
1124 #undef FUNC_NAME
1125
1126 static void
1127 fat_cond_signal (fat_cond *c)
1128 {
1129 fprintf (stderr, "cond signal on %p\n", &c->lock);
1130
1131 scm_i_scm_pthread_mutex_lock (&c->lock);
1132 unblock_from_queue (c->waiting);
1133 scm_i_pthread_mutex_unlock (&c->lock);
1134 }
1135
1136 SCM_DEFINE (scm_signal_condition_variable, "signal-condition-variable", 1, 0, 0,
1137 (SCM cv),
1138 "Wake up one thread that is waiting for @var{cv}")
1139 #define FUNC_NAME s_scm_signal_condition_variable
1140 {
1141 SCM_VALIDATE_CONDVAR (1, cv);
1142 fat_cond_signal (SCM_CONDVAR_DATA (cv));
1143 return SCM_BOOL_T;
1144 }
1145 #undef FUNC_NAME
1146
1147 static void
1148 fat_cond_broadcast (fat_cond *c)
1149 {
1150 scm_i_scm_pthread_mutex_lock (&c->lock);
1151 while (scm_is_true (unblock_from_queue (c->waiting)))
1152 ;
1153 scm_i_pthread_mutex_unlock (&c->lock);
1154 }
1155
1156 SCM_DEFINE (scm_broadcast_condition_variable, "broadcast-condition-variable", 1, 0, 0,
1157 (SCM cv),
1158 "Wake up all threads that are waiting for @var{cv}. ")
1159 #define FUNC_NAME s_scm_broadcast_condition_variable
1160 {
1161 SCM_VALIDATE_CONDVAR (1, cv);
1162 fat_cond_broadcast (SCM_CONDVAR_DATA (cv));
1163 return SCM_BOOL_T;
1164 }
1165 #undef FUNC_NAME
1166
1167 /*** Marking stacks */
1168
1169 /* XXX - what to do with this? Do we need to handle this for blocked
1170 threads as well?
1171 */
1172 #ifdef __ia64__
1173 # define SCM_MARK_BACKING_STORE() do { \
1174 ucontext_t ctx; \
1175 SCM_STACKITEM * top, * bot; \
1176 getcontext (&ctx); \
1177 scm_mark_locations ((SCM_STACKITEM *) &ctx.uc_mcontext, \
1178 ((size_t) (sizeof (SCM_STACKITEM) - 1 + sizeof ctx.uc_mcontext) \
1179 / sizeof (SCM_STACKITEM))); \
1180 bot = (SCM_STACKITEM *) __libc_ia64_register_backing_store_base; \
1181 top = (SCM_STACKITEM *) ctx.uc_mcontext.sc_ar_bsp; \
1182 scm_mark_locations (bot, top - bot); } while (0)
1183 #else
1184 # define SCM_MARK_BACKING_STORE()
1185 #endif
1186
1187 void
1188 scm_threads_mark_stacks (void)
1189 {
1190 scm_i_thread *t;
1191 for (t = all_threads; t; t = t->next_thread)
1192 {
1193 /* Check that thread has indeed been suspended.
1194 */
1195 assert (t->top);
1196
1197 scm_gc_mark (t->handle);
1198
1199 #if SCM_STACK_GROWS_UP
1200 scm_mark_locations (t->base, t->top - t->base);
1201 #else
1202 scm_mark_locations (t->top, t->base - t->top);
1203 #endif
1204 scm_mark_locations ((SCM_STACKITEM *) t->regs,
1205 ((size_t) sizeof(t->regs)
1206 / sizeof (SCM_STACKITEM)));
1207 }
1208
1209 SCM_MARK_BACKING_STORE ();
1210 }
1211
1212 /*** Select */
1213
1214 int
1215 scm_std_select (int nfds,
1216 SELECT_TYPE *readfds,
1217 SELECT_TYPE *writefds,
1218 SELECT_TYPE *exceptfds,
1219 struct timeval *timeout)
1220 {
1221 fd_set my_readfds;
1222 int res, eno, wakeup_fd;
1223 scm_i_thread *t = SCM_I_CURRENT_THREAD;
1224 scm_t_guile_ticket ticket;
1225
1226 if (readfds == NULL)
1227 {
1228 FD_ZERO (&my_readfds);
1229 readfds = &my_readfds;
1230 }
1231
1232 while (scm_i_setup_sleep (t, SCM_BOOL_F, NULL, t->sleep_pipe[1]))
1233 SCM_TICK;
1234
1235 wakeup_fd = t->sleep_pipe[0];
1236 ticket = scm_leave_guile ();
1237 FD_SET (wakeup_fd, readfds);
1238 if (wakeup_fd >= nfds)
1239 nfds = wakeup_fd+1;
1240 res = select (nfds, readfds, writefds, exceptfds, timeout);
1241 t->sleep_fd = -1;
1242 eno = errno;
1243 scm_enter_guile (ticket);
1244
1245 scm_i_reset_sleep (t);
1246
1247 if (res > 0 && FD_ISSET (wakeup_fd, readfds))
1248 {
1249 char dummy;
1250 read (wakeup_fd, &dummy, 1);
1251 FD_CLR (wakeup_fd, readfds);
1252 res -= 1;
1253 if (res == 0)
1254 {
1255 eno = EINTR;
1256 res = -1;
1257 }
1258 }
1259 errno = eno;
1260 return res;
1261 }
1262
1263 /* Convenience API for blocking while in guile mode. */
1264
1265 #if SCM_USE_PTHREAD_THREADS
1266
1267 int
1268 scm_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
1269 {
1270 scm_t_guile_ticket t = scm_leave_guile ();
1271 int res = scm_i_pthread_mutex_lock (mutex);
1272 scm_enter_guile (t);
1273 return res;
1274 }
1275
1276 static void
1277 unlock (void *data)
1278 {
1279 scm_i_pthread_mutex_unlock ((scm_i_pthread_mutex_t *)data);
1280 }
1281
1282 void
1283 scm_frame_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
1284 {
1285 scm_i_scm_pthread_mutex_lock (mutex);
1286 scm_frame_unwind_handler (unlock, mutex, SCM_F_WIND_EXPLICITLY);
1287 }
1288
1289 int
1290 scm_pthread_cond_wait (scm_i_pthread_cond_t *cond, scm_i_pthread_mutex_t *mutex)
1291 {
1292 scm_t_guile_ticket t = scm_leave_guile ();
1293 int res = scm_i_pthread_cond_wait (cond, mutex);
1294 scm_enter_guile (t);
1295 return res;
1296 }
1297
1298 int
1299 scm_pthread_cond_timedwait (scm_i_pthread_cond_t *cond,
1300 scm_i_pthread_mutex_t *mutex,
1301 const scm_t_timespec *wt)
1302 {
1303 scm_t_guile_ticket t = scm_leave_guile ();
1304 int res = scm_i_pthread_cond_timedwait (cond, mutex, wt);
1305 scm_enter_guile (t);
1306 return res;
1307 }
1308
1309 #endif
1310
1311 unsigned long
1312 scm_std_usleep (unsigned long usecs)
1313 {
1314 struct timeval tv;
1315 tv.tv_usec = usecs % 1000000;
1316 tv.tv_sec = usecs / 1000000;
1317 scm_std_select (0, NULL, NULL, NULL, &tv);
1318 return tv.tv_sec * 1000000 + tv.tv_usec;
1319 }
1320
1321 unsigned int
1322 scm_std_sleep (unsigned int secs)
1323 {
1324 struct timeval tv;
1325 tv.tv_usec = 0;
1326 tv.tv_sec = secs;
1327 scm_std_select (0, NULL, NULL, NULL, &tv);
1328 return tv.tv_sec;
1329 }
1330
1331 /*** Misc */
1332
1333 SCM_DEFINE (scm_current_thread, "current-thread", 0, 0, 0,
1334 (void),
1335 "Return the thread that called this function.")
1336 #define FUNC_NAME s_scm_current_thread
1337 {
1338 return SCM_I_CURRENT_THREAD->handle;
1339 }
1340 #undef FUNC_NAME
1341
1342 static SCM
1343 scm_c_make_list (size_t n, SCM fill)
1344 {
1345 SCM res = SCM_EOL;
1346 while (n-- > 0)
1347 res = scm_cons (fill, res);
1348 return res;
1349 }
1350
1351 SCM_DEFINE (scm_all_threads, "all-threads", 0, 0, 0,
1352 (void),
1353 "Return a list of all threads.")
1354 #define FUNC_NAME s_scm_all_threads
1355 {
1356 /* We can not allocate while holding the thread_admin_mutex because
1357 of the way GC is done.
1358 */
1359 int n = thread_count;
1360 scm_i_thread *t;
1361 SCM list = scm_c_make_list (n, SCM_UNSPECIFIED), *l;
1362
1363 scm_i_pthread_mutex_lock (&thread_admin_mutex);
1364 l = &list;
1365 for (t = all_threads; t && n > 0; t = t->next_thread)
1366 {
1367 SCM_SETCAR (*l, t->handle);
1368 l = SCM_CDRLOC (*l);
1369 n--;
1370 }
1371 *l = SCM_EOL;
1372 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
1373 return list;
1374 }
1375 #undef FUNC_NAME
1376
1377 SCM_DEFINE (scm_thread_exited_p, "thread-exited?", 1, 0, 0,
1378 (SCM thread),
1379 "Return @code{#t} iff @var{thread} has exited.\n")
1380 #define FUNC_NAME s_scm_thread_exited_p
1381 {
1382 return scm_from_bool (scm_c_thread_exited_p (thread));
1383 }
1384 #undef FUNC_NAME
1385
1386 int
1387 scm_c_thread_exited_p (SCM thread)
1388 #define FUNC_NAME s_scm_thread_exited_p
1389 {
1390 scm_i_thread *t;
1391 SCM_VALIDATE_THREAD (1, thread);
1392 t = SCM_I_THREAD_DATA (thread);
1393 return t->exited;
1394 }
1395 #undef FUNC_NAME
1396
1397 static scm_i_pthread_cond_t wake_up_cond;
1398 int scm_i_thread_go_to_sleep;
1399 static int threads_initialized_p = 0;
1400 static int sleep_level = 0;
1401
1402 void
1403 scm_i_thread_put_to_sleep ()
1404 {
1405 if (threads_initialized_p)
1406 {
1407 scm_i_thread *t;
1408
1409 scm_leave_guile ();
1410 scm_i_pthread_mutex_lock (&thread_admin_mutex);
1411
1412 if (sleep_level == 0)
1413 {
1414 /* Signal all threads to go to sleep
1415 */
1416 scm_i_thread_go_to_sleep = 1;
1417 for (t = all_threads; t; t = t->next_thread)
1418 scm_i_pthread_mutex_lock (&t->heap_mutex);
1419 scm_i_thread_go_to_sleep = 0;
1420 }
1421 else
1422 {
1423 /* We are already single threaded. Suspend again to update
1424 the recorded stack information.
1425 */
1426 suspend ();
1427 }
1428 sleep_level += 1;
1429
1430 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
1431 }
1432 }
1433
1434 void
1435 scm_i_thread_invalidate_freelists ()
1436 {
1437 /* thread_admin_mutex is already locked. */
1438
1439 scm_i_thread *t;
1440 for (t = all_threads; t; t = t->next_thread)
1441 if (t != SCM_I_CURRENT_THREAD)
1442 t->clear_freelists_p = 1;
1443 }
1444
1445 void
1446 scm_i_thread_wake_up ()
1447 {
1448 if (threads_initialized_p)
1449 {
1450 scm_i_thread *t;
1451 scm_i_pthread_mutex_lock (&thread_admin_mutex);
1452
1453 sleep_level -= 1;
1454 if (sleep_level == 0)
1455 {
1456 scm_i_pthread_cond_broadcast (&wake_up_cond);
1457 for (t = all_threads; t; t = t->next_thread)
1458 scm_i_pthread_mutex_unlock (&t->heap_mutex);
1459 }
1460
1461 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
1462 scm_enter_guile ((scm_t_guile_ticket) SCM_I_CURRENT_THREAD);
1463 }
1464 }
1465
1466 void
1467 scm_i_thread_sleep_for_gc ()
1468 {
1469 scm_i_thread *t = suspend ();
1470 scm_i_pthread_cond_wait (&wake_up_cond, &t->heap_mutex);
1471 resume (t);
1472 }
1473
1474 static void
1475 put_to_sleep (void *unused)
1476 {
1477 scm_i_thread_put_to_sleep ();
1478 }
1479
1480 static void
1481 wake_up (void *unused)
1482 {
1483 scm_i_thread_wake_up ();
1484 }
1485
1486 void
1487 scm_i_frame_single_threaded ()
1488 {
1489 scm_frame_rewind_handler (put_to_sleep, NULL, SCM_F_WIND_EXPLICITLY);
1490 scm_frame_unwind_handler (wake_up, NULL, SCM_F_WIND_EXPLICITLY);
1491 }
1492
1493 scm_i_pthread_mutex_t scm_i_critical_section_mutex =
1494 SCM_I_PTHREAD_MUTEX_INITIALIZER;
1495
1496 void
1497 scm_frame_critical_section ()
1498 {
1499 scm_i_frame_pthread_mutex_lock (&scm_i_critical_section_mutex);
1500 scm_frame_block_asyncs ();
1501 }
1502
1503 /*** Initialization */
1504
1505 scm_i_pthread_key_t scm_i_freelist, scm_i_freelist2;
1506 scm_i_pthread_mutex_t scm_i_misc_mutex;
1507
1508 void
1509 scm_threads_prehistory (SCM_STACKITEM *base)
1510 {
1511 scm_i_pthread_mutex_init (&thread_admin_mutex, NULL);
1512 scm_i_pthread_mutex_init (&scm_i_misc_mutex, NULL);
1513 scm_i_pthread_cond_init (&wake_up_cond, NULL);
1514 scm_i_pthread_mutex_init (&scm_i_critical_section_mutex, NULL);
1515 scm_i_pthread_key_create (&scm_i_freelist, NULL);
1516 scm_i_pthread_key_create (&scm_i_freelist2, NULL);
1517
1518 guilify_self_1 (base);
1519 }
1520
1521 scm_t_bits scm_tc16_thread;
1522 scm_t_bits scm_tc16_mutex;
1523 scm_t_bits scm_tc16_condvar;
1524
1525 void
1526 scm_init_threads ()
1527 {
1528 scm_tc16_thread = scm_make_smob_type ("thread", sizeof (scm_i_thread));
1529 scm_set_smob_mark (scm_tc16_thread, thread_mark);
1530 scm_set_smob_print (scm_tc16_thread, thread_print);
1531 scm_set_smob_free (scm_tc16_thread, thread_free);
1532
1533 scm_tc16_mutex = scm_make_smob_type ("mutex", sizeof (fat_mutex));
1534 scm_set_smob_mark (scm_tc16_mutex, fat_mutex_mark);
1535 scm_set_smob_print (scm_tc16_mutex, fat_mutex_print);
1536 scm_set_smob_free (scm_tc16_mutex, fat_mutex_free);
1537
1538 scm_tc16_condvar = scm_make_smob_type ("condition-variable",
1539 sizeof (fat_cond));
1540 scm_set_smob_mark (scm_tc16_condvar, fat_cond_mark);
1541 scm_set_smob_print (scm_tc16_condvar, fat_cond_print);
1542 scm_set_smob_free (scm_tc16_condvar, fat_cond_free);
1543
1544 scm_i_default_dynamic_state = SCM_BOOL_F;
1545 guilify_self_2 (SCM_BOOL_F);
1546 threads_initialized_p = 1;
1547 }
1548
1549 void
1550 scm_init_threads_default_dynamic_state ()
1551 {
1552 SCM state = scm_make_dynamic_state (scm_current_dynamic_state ());
1553 scm_i_default_dynamic_state = scm_permanent_object (state);
1554 }
1555
1556 void
1557 scm_init_thread_procs ()
1558 {
1559 #include "libguile/threads.x"
1560 }
1561
1562 /*
1563 Local Variables:
1564 c-file-style: "gnu"
1565 End:
1566 */