Synchronized docstrings.
[bpt/guile.git] / libguile / threads.c
CommitLineData
38466059 1/* Copyright (C) 1995,1996,1997,1998,2000,2001, 2002, 2003, 2004 Free Software Foundation, Inc.
7bfd3b9e 2 *
73be1d9e
MV
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.
7bfd3b9e 7 *
73be1d9e
MV
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.
7bfd3b9e 12 *
73be1d9e
MV
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 */
1bbd0b84 17
1bbd0b84 18
7bfd3b9e
JB
19\f
20
9de87eea 21#define _GNU_SOURCE
76da80e7 22
1810dc4e
RB
23#include "libguile/_scm.h"
24
fcc5d734 25#if HAVE_UNISTD_H
d823b11b 26#include <unistd.h>
fcc5d734 27#endif
d823b11b
MV
28#include <stdio.h>
29#include <assert.h>
fcc5d734 30#if HAVE_SYS_TIME_H
d823b11b 31#include <sys/time.h>
fcc5d734 32#endif
5f05c406 33
d823b11b
MV
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"
a0599745 40#include "libguile/dynwind.h"
d823b11b 41#include "libguile/iselect.h"
9de87eea
MV
42#include "libguile/fluids.h"
43#include "libguile/continuations.h"
44#include "libguile/init.h"
7bfd3b9e 45
d823b11b 46/*** Queues */
7bfd3b9e 47
9de87eea
MV
48/* Make an empty queue data structure.
49 */
d823b11b
MV
50static SCM
51make_queue ()
52{
53 return scm_cons (SCM_EOL, SCM_EOL);
54}
7bfd3b9e 55
9de87eea
MV
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 */
d823b11b
MV
59static SCM
60enqueue (SCM q, SCM t)
61{
62 SCM c = scm_cons (t, SCM_EOL);
d2e53ed6 63 if (scm_is_null (SCM_CDR (q)))
d823b11b
MV
64 SCM_SETCDR (q, c);
65 else
66 SCM_SETCDR (SCM_CAR (q), c);
67 SCM_SETCAR (q, c);
68 return c;
69}
7bfd3b9e 70
9de87eea
MV
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*/
76static int
d823b11b
MV
77remqueue (SCM q, SCM c)
78{
79 SCM p, prev = q;
d2e53ed6 80 for (p = SCM_CDR (q); !scm_is_null (p); p = SCM_CDR (p))
d823b11b 81 {
bc36d050 82 if (scm_is_eq (p, c))
d823b11b 83 {
bc36d050 84 if (scm_is_eq (c, SCM_CAR (q)))
d823b11b
MV
85 SCM_SETCAR (q, SCM_CDR (c));
86 SCM_SETCDR (prev, SCM_CDR (c));
9de87eea 87 return 1;
d823b11b
MV
88 }
89 prev = p;
90 }
9de87eea 91 return 0;
d823b11b
MV
92}
93
9de87eea
MV
94/* Remove the front-most element from the queue Q and return it.
95 Return SCM_BOOL_F when Q is empty.
96*/
d823b11b
MV
97static SCM
98dequeue (SCM q)
99{
100 SCM c = SCM_CDR (q);
d2e53ed6 101 if (scm_is_null (c))
d823b11b
MV
102 return SCM_BOOL_F;
103 else
104 {
105 SCM_SETCDR (q, SCM_CDR (c));
d2e53ed6 106 if (scm_is_null (SCM_CDR (q)))
d823b11b
MV
107 SCM_SETCAR (q, SCM_EOL);
108 return SCM_CAR (c);
109 }
110}
7bfd3b9e 111
9de87eea 112/*** Thread smob routines */
76da80e7 113
d823b11b
MV
114static SCM
115thread_mark (SCM obj)
116{
9de87eea 117 scm_i_thread *t = SCM_I_THREAD_DATA (obj);
d823b11b 118 scm_gc_mark (t->result);
9de87eea
MV
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;
d823b11b
MV
125}
126
127static int
128thread_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
129{
9de87eea 130 scm_i_thread *t = SCM_I_THREAD_DATA (exp);
d823b11b 131 scm_puts ("#<thread ", port);
9de87eea 132 scm_uintprint ((size_t)t->pthread, 10, port);
1b92fb6b 133 scm_puts (" (", port);
0345e278 134 scm_uintprint ((scm_t_bits)t, 16, port);
1b92fb6b 135 scm_puts (")>", port);
d823b11b
MV
136 return 1;
137}
138
139static size_t
140thread_free (SCM obj)
141{
9de87eea
MV
142 scm_i_thread *t = SCM_I_THREAD_DATA (obj);
143 assert (t->exited);
d823b11b
MV
144 scm_gc_free (t, sizeof (*t), "thread");
145 return 0;
146}
147
9de87eea 148/*** Blocking on queues. */
f7eca35d 149
9de87eea
MV
150/* See also scm_i_queue_async_cell for how such a block is
151 interrputed.
152*/
d823b11b 153
9de87eea
MV
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*/
176static int
177block_self (SCM queue, SCM sleep_object, scm_i_pthread_mutex_t *mutex,
178 const scm_t_timespec *waittime)
76da80e7 179{
9de87eea
MV
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;
76da80e7 206}
9de87eea
MV
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 */
212static SCM
213unblock_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
224scm_i_pthread_key_t scm_i_thread_key;
225
d823b11b 226static void
9de87eea 227resume (scm_i_thread *t)
d823b11b 228{
d823b11b 229 t->top = NULL;
9bc4701c
MD
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 }
d823b11b
MV
236}
237
76da80e7 238void
9de87eea 239scm_enter_guile (scm_t_guile_ticket ticket)
d823b11b 240{
9de87eea
MV
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 }
d823b11b
MV
247}
248
9de87eea
MV
249static scm_i_thread *
250suspend (void)
d823b11b 251{
9de87eea 252 scm_i_thread *t = SCM_I_CURRENT_THREAD;
d823b11b
MV
253
254 /* record top of stack for the GC */
9de87eea 255 t->top = SCM_STACK_PTR (&t);
d823b11b
MV
256 /* save registers. */
257 SCM_FLUSH_REGISTER_WINDOWS;
9de87eea
MV
258 setjmp (t->regs);
259 return t;
d823b11b
MV
260}
261
9de87eea
MV
262scm_t_guile_ticket
263scm_leave_guile ()
d823b11b 264{
9de87eea
MV
265 scm_i_thread *t = suspend ();
266 scm_i_pthread_mutex_unlock (&t->heap_mutex);
267 return (scm_t_guile_ticket) t;
d823b11b
MV
268}
269
9de87eea
MV
270static scm_i_pthread_mutex_t thread_admin_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
271static scm_i_thread *all_threads = NULL;
272static int thread_count;
273
274static SCM scm_i_default_dynamic_state;
275
276/* Perform first stage of thread initialisation, in non-guile mode.
d823b11b 277 */
9de87eea
MV
278static void
279guilify_self_1 (SCM_STACKITEM *base)
d823b11b 280{
9de87eea
MV
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);
d823b11b
MV
319}
320
9de87eea 321/* Perform second stage of thread initialisation, in guile mode.
d823b11b 322 */
9de87eea
MV
323static void
324guilify_self_2 (SCM parent)
d823b11b 325{
9de87eea
MV
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;
d823b11b
MV
340}
341
9de87eea 342/* Perform thread tear-down, in guile mode.
d823b11b 343 */
9de87eea
MV
344static void *
345do_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
d823b11b 367static void
9de87eea 368on_thread_exit (void *v)
d823b11b 369{
9de87eea
MV
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);
d823b11b
MV
373}
374
9de87eea 375static scm_i_pthread_once_t init_thread_key_once = SCM_I_PTHREAD_ONCE_INIT;
d823b11b 376
9de87eea
MV
377static void
378init_thread_key (void)
379{
380 scm_i_pthread_key_create (&scm_i_thread_key, on_thread_exit);
381}
d823b11b 382
9de87eea
MV
383/* Perform any initializations necessary to bring the current thread
384 into guile mode, initializing Guile itself, if necessary.
a54a94b3 385
9de87eea
MV
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
395static int
396scm_i_init_thread_for_guile (SCM_STACKITEM *base, SCM parent)
d823b11b 397{
9de87eea
MV
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 }
d823b11b
MV
444}
445
9de87eea
MV
446#ifdef HAVE_LIBC_STACK_END
447
448extern 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
455static SCM_STACKITEM *
456get_thread_stack_base ()
d823b11b 457{
9de87eea
MV
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 }
a54a94b3
MV
481}
482
9de87eea
MV
483#endif /* HAVE_PTHREAD_ATTR_GETSTACK */
484
485#else /* !SCM_USE_PTHREAD_THREADS */
486
487#define HAVE_GET_THREAD_STACK_BASE
488
489static SCM_STACKITEM *
490get_thread_stack_base ()
a54a94b3 491{
9de87eea 492 return __libc_stack_end;
d823b11b
MV
493}
494
9de87eea
MV
495#endif /* !SCM_USE_PTHREAD_THREADS */
496#endif /* HAVE_LIBC_STACK_END */
497
498#ifdef HAVE_GET_THREAD_STACK_BASE
499
500void
501scm_init_guile ()
d823b11b 502{
9de87eea
MV
503 scm_i_init_thread_for_guile (get_thread_stack_base (),
504 scm_i_default_dynamic_state);
d823b11b
MV
505}
506
9de87eea
MV
507#endif
508
509void *
510scm_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
516void *
517scm_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
530void *
531scm_without_guile (void *(*func)(void *), void *data)
d823b11b 532{
9de87eea
MV
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
543typedef struct {
544 SCM parent;
545 SCM thunk;
546 SCM handler;
76da80e7 547 SCM thread;
9de87eea
MV
548 scm_i_pthread_mutex_t mutex;
549 scm_i_pthread_cond_t cond;
550} launch_data;
d823b11b 551
9de87eea
MV
552static void *
553really_launch (void *d)
554{
555 launch_data *data = (launch_data *)d;
556 SCM thunk = data->thunk, handler = data->handler;
557 scm_i_thread *t;
d823b11b 558
9de87eea 559 t = SCM_I_CURRENT_THREAD;
a54a94b3 560
9de87eea
MV
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;
d823b11b
MV
572}
573
9de87eea
MV
574static void *
575launch_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
583SCM_DEFINE (scm_call_with_new_thread, "call-with-new-thread", 1, 1, 0,
d823b11b 584 (SCM thunk, SCM handler),
9de87eea
MV
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.")
d823b11b
MV
595#define FUNC_NAME s_scm_call_with_new_thread
596{
9de87eea
MV
597 launch_data data;
598 scm_i_pthread_t id;
599 int err;
d823b11b 600
9de87eea
MV
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;
d823b11b
MV
624}
625#undef FUNC_NAME
626
9de87eea
MV
627typedef 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
638static void *
639really_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
663static void *
664spawn_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
672SCM
673scm_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
29717c89
MD
703SCM_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{
9de87eea 708 return scm_from_bool (scm_i_sched_yield ());
29717c89
MD
709}
710#undef FUNC_NAME
711
d823b11b 712SCM_DEFINE (scm_join_thread, "join-thread", 1, 0, 0,
5f05c406 713 (SCM thread),
d823b11b
MV
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
5f05c406 717{
9de87eea 718 scm_i_thread *t;
d823b11b
MV
719 SCM res;
720
721 SCM_VALIDATE_THREAD (1, thread);
9de87eea 722 if (scm_is_eq (scm_current_thread (), thread))
d823b11b
MV
723 SCM_MISC_ERROR ("can not join the current thread", SCM_EOL);
724
9de87eea
MV
725 scm_i_scm_pthread_mutex_lock (&thread_admin_mutex);
726
727 t = SCM_I_THREAD_DATA (thread);
d823b11b
MV
728 if (!t->exited)
729 {
9de87eea
MV
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 }
d823b11b
MV
739 }
740 res = t->result;
9de87eea
MV
741
742 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
d823b11b 743 return res;
5f05c406
MV
744}
745#undef FUNC_NAME
746
9de87eea 747/*** Fat mutexes */
4079f87e 748
d823b11b
MV
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
9de87eea
MV
751 asyncs) and we might want to add things that are nice for
752 debugging.
d823b11b 753*/
4079f87e 754
9de87eea
MV
755typedef struct {
756 scm_i_pthread_mutex_t lock;
d823b11b 757 SCM owner;
9de87eea
MV
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))
5f05c406 765
d823b11b 766static SCM
9de87eea 767fat_mutex_mark (SCM mx)
d823b11b 768{
9de87eea 769 fat_mutex *m = SCM_MUTEX_DATA (mx);
d823b11b
MV
770 scm_gc_mark (m->owner);
771 return m->waiting;
772}
4079f87e 773
9de87eea
MV
774static size_t
775fat_mutex_free (SCM mx)
76da80e7 776{
9de87eea
MV
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");
76da80e7
MV
780 return 0;
781}
782
783static int
9de87eea 784fat_mutex_print (SCM mx, SCM port, scm_print_state *pstate SCM_UNUSED)
76da80e7 785{
9de87eea
MV
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;
76da80e7
MV
791}
792
76da80e7 793static SCM
9de87eea 794make_fat_mutex (int recursive)
76da80e7 795{
9de87eea
MV
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;
76da80e7
MV
807}
808
9de87eea 809SCM_DEFINE (scm_make_mutex, "make-mutex", 0, 0, 0,
76da80e7 810 (void),
9de87eea
MV
811 "Create a new mutex. ")
812#define FUNC_NAME s_scm_make_mutex
76da80e7 813{
9de87eea 814 return make_fat_mutex (0);
76da80e7
MV
815}
816#undef FUNC_NAME
817
9de87eea 818SCM_DEFINE (scm_make_recursive_mutex, "make-recursive-mutex", 0, 0, 0,
9bc4701c 819 (void),
9de87eea
MV
820 "Create a new recursive mutex. ")
821#define FUNC_NAME s_scm_make_recursive_mutex
9bc4701c 822{
9de87eea 823 return make_fat_mutex (1);
9bc4701c
MD
824}
825#undef FUNC_NAME
826
9de87eea
MV
827static char *
828fat_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
9bc4701c
MD
860SCM_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{
9bc4701c 869 SCM_VALIDATE_MUTEX (1, mx);
9de87eea 870 char *msg;
76da80e7 871
9de87eea
MV
872 msg = fat_mutex_lock (mx);
873 if (msg)
874 scm_misc_error (NULL, msg, SCM_EOL);
76da80e7 875 return SCM_BOOL_T;
9bc4701c 876}
76da80e7 877#undef FUNC_NAME
9bc4701c 878
9de87eea
MV
879static char *
880fat_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
9bc4701c 902SCM_DEFINE (scm_try_mutex, "try-mutex", 1, 0, 0,
ba1b7223 903 (SCM mutex),
9bc4701c
MD
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{
9de87eea
MV
908 char *msg;
909 int res;
910
ba1b7223 911 SCM_VALIDATE_MUTEX (1, mutex);
9bc4701c 912
ba1b7223 913 msg = fat_mutex_trylock (SCM_MUTEX_DATA (mutex), &res);
9de87eea
MV
914 if (msg)
915 scm_misc_error (NULL, msg, SCM_EOL);
916 return scm_from_bool (res);
917}
918#undef FUNC_NAME
76da80e7 919
9de87eea
MV
920static char *
921fat_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 ()))
9bc4701c 927 {
9de87eea
MV
928 if (scm_is_false (m->owner))
929 msg = "mutex not locked";
930 else
931 msg = "mutex not locked by current thread";
9bc4701c 932 }
9de87eea
MV
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;
9bc4701c 940}
9bc4701c
MD
941
942SCM_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{
9de87eea 953 char *msg;
9bc4701c
MD
954 SCM_VALIDATE_MUTEX (1, mx);
955
9de87eea
MV
956 msg = fat_mutex_unlock (SCM_MUTEX_DATA (mx));
957 if (msg)
958 scm_misc_error (NULL, msg, SCM_EOL);
9bc4701c
MD
959 return SCM_BOOL_T;
960}
961#undef FUNC_NAME
962
9de87eea
MV
963#if 0
964
965SCM_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
975SCM_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
990typedef 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
998static SCM
999fat_cond_mark (SCM cv)
1000{
1001 fat_cond *c = SCM_CONDVAR_DATA (cv);
1002 return c->waiting;
1003}
1004
1005static size_t
1006fat_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
1014static int
1015fat_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}
9bc4701c 1023
d823b11b
MV
1024SCM_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
5f05c406 1028{
9de87eea
MV
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 ();
d823b11b 1037 return cv;
5f05c406 1038}
d823b11b 1039#undef FUNC_NAME
5f05c406 1040
9de87eea
MV
1041static int
1042fat_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
d823b11b
MV
1089SCM_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
5f05c406 1101{
9de87eea 1102 scm_t_timespec waittime, *waitptr = NULL;
d823b11b
MV
1103
1104 SCM_VALIDATE_CONDVAR (1, cv);
1105 SCM_VALIDATE_MUTEX (2, mx);
9bc4701c 1106
d823b11b
MV
1107 if (!SCM_UNBNDP (t))
1108 {
d2e53ed6 1109 if (scm_is_pair (t))
d823b11b 1110 {
9de87eea
MV
1111 waittime.tv_sec = scm_to_ulong (SCM_CAR (t));
1112 waittime.tv_nsec = scm_to_ulong (SCM_CAR (t)) * 1000;
d823b11b
MV
1113 }
1114 else
1115 {
9de87eea 1116 waittime.tv_sec = scm_to_ulong (t);
d823b11b
MV
1117 waittime.tv_nsec = 0;
1118 }
9de87eea 1119 waitptr = &waittime;
d823b11b
MV
1120 }
1121
9de87eea 1122 return scm_from_bool (fat_cond_timedwait (cv, mx, waitptr));
5f05c406 1123}
d823b11b 1124#undef FUNC_NAME
5f05c406 1125
9de87eea
MV
1126static void
1127fat_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
d823b11b
MV
1136SCM_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
5f05c406 1140{
d823b11b 1141 SCM_VALIDATE_CONDVAR (1, cv);
9de87eea 1142 fat_cond_signal (SCM_CONDVAR_DATA (cv));
d823b11b 1143 return SCM_BOOL_T;
5f05c406 1144}
d823b11b 1145#undef FUNC_NAME
5f05c406 1146
9de87eea
MV
1147static void
1148fat_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
d823b11b
MV
1156SCM_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
5f05c406 1160{
d823b11b 1161 SCM_VALIDATE_CONDVAR (1, cv);
9de87eea 1162 fat_cond_broadcast (SCM_CONDVAR_DATA (cv));
d823b11b 1163 return SCM_BOOL_T;
5f05c406 1164}
d823b11b 1165#undef FUNC_NAME
5f05c406 1166
d823b11b
MV
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
1187void
1188scm_threads_mark_stacks (void)
5f05c406 1189{
9de87eea
MV
1190 scm_i_thread *t;
1191 for (t = all_threads; t; t = t->next_thread)
d823b11b 1192 {
9de87eea
MV
1193 /* Check that thread has indeed been suspended.
1194 */
1195 assert (t->top);
6087fad9 1196
9de87eea 1197 scm_gc_mark (t->handle);
6087fad9 1198
d028af45 1199#if SCM_STACK_GROWS_UP
9de87eea 1200 scm_mark_locations (t->base, t->top - t->base);
d823b11b 1201#else
9de87eea 1202 scm_mark_locations (t->top, t->base - t->top);
d823b11b 1203#endif
6087fad9
MV
1204 scm_mark_locations ((SCM_STACKITEM *) t->regs,
1205 ((size_t) sizeof(t->regs)
1206 / sizeof (SCM_STACKITEM)));
d823b11b 1207 }
59152722
MV
1208
1209 SCM_MARK_BACKING_STORE ();
5f05c406
MV
1210}
1211
d823b11b
MV
1212/*** Select */
1213
911782b7 1214int
9de87eea
MV
1215scm_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;
d823b11b 1242 eno = errno;
9de87eea
MV
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 }
d823b11b
MV
1259 errno = eno;
1260 return res;
5f05c406
MV
1261}
1262
9de87eea 1263/* Convenience API for blocking while in guile mode. */
76da80e7 1264
9de87eea 1265#if SCM_USE_PTHREAD_THREADS
92e64b87 1266
9bc4701c 1267int
9de87eea 1268scm_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
9bc4701c 1269{
9de87eea
MV
1270 scm_t_guile_ticket t = scm_leave_guile ();
1271 int res = scm_i_pthread_mutex_lock (mutex);
1272 scm_enter_guile (t);
9bc4701c
MD
1273 return res;
1274}
1275
9de87eea
MV
1276static void
1277unlock (void *data)
28d52ebb 1278{
9de87eea 1279 scm_i_pthread_mutex_unlock ((scm_i_pthread_mutex_t *)data);
28d52ebb
MD
1280}
1281
1282void
9de87eea 1283scm_frame_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
28d52ebb 1284{
9de87eea
MV
1285 scm_i_scm_pthread_mutex_lock (mutex);
1286 scm_frame_unwind_handler (unlock, mutex, SCM_F_WIND_EXPLICITLY);
28d52ebb
MD
1287}
1288
9bc4701c 1289int
9de87eea 1290scm_pthread_cond_wait (scm_i_pthread_cond_t *cond, scm_i_pthread_mutex_t *mutex)
9bc4701c 1291{
9de87eea
MV
1292 scm_t_guile_ticket t = scm_leave_guile ();
1293 int res = scm_i_pthread_cond_wait (cond, mutex);
1294 scm_enter_guile (t);
9bc4701c
MD
1295 return res;
1296}
9bc4701c 1297
76da80e7 1298int
9de87eea
MV
1299scm_pthread_cond_timedwait (scm_i_pthread_cond_t *cond,
1300 scm_i_pthread_mutex_t *mutex,
1301 const scm_t_timespec *wt)
76da80e7 1302{
9de87eea
MV
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;
76da80e7
MV
1307}
1308
9de87eea 1309#endif
76da80e7 1310
d823b11b 1311unsigned long
9de87eea 1312scm_std_usleep (unsigned long usecs)
5f05c406 1313{
d823b11b
MV
1314 struct timeval tv;
1315 tv.tv_usec = usecs % 1000000;
1316 tv.tv_sec = usecs / 1000000;
9de87eea
MV
1317 scm_std_select (0, NULL, NULL, NULL, &tv);
1318 return tv.tv_sec * 1000000 + tv.tv_usec;
5f05c406
MV
1319}
1320
9de87eea
MV
1321unsigned int
1322scm_std_sleep (unsigned int secs)
6c214b62 1323{
d823b11b
MV
1324 struct timeval tv;
1325 tv.tv_usec = 0;
1326 tv.tv_sec = secs;
9de87eea 1327 scm_std_select (0, NULL, NULL, NULL, &tv);
d823b11b 1328 return tv.tv_sec;
6c214b62
MD
1329}
1330
d823b11b
MV
1331/*** Misc */
1332
1333SCM_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{
9de87eea 1338 return SCM_I_CURRENT_THREAD->handle;
d823b11b
MV
1339}
1340#undef FUNC_NAME
1341
9de87eea
MV
1342static SCM
1343scm_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
d823b11b
MV
1351SCM_DEFINE (scm_all_threads, "all-threads", 0, 0, 0,
1352 (void),
1353 "Return a list of all threads.")
9bc4701c 1354#define FUNC_NAME s_scm_all_threads
d823b11b 1355{
9de87eea
MV
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;
d823b11b 1362
9de87eea
MV
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;
d823b11b 1374}
9de87eea 1375#undef FUNC_NAME
d823b11b
MV
1376
1377SCM_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{
7888309b 1382 return scm_from_bool (scm_c_thread_exited_p (thread));
d823b11b
MV
1383}
1384#undef FUNC_NAME
1385
911782b7 1386int
d823b11b
MV
1387scm_c_thread_exited_p (SCM thread)
1388#define FUNC_NAME s_scm_thread_exited_p
5f05c406 1389{
9de87eea 1390 scm_i_thread *t;
d823b11b 1391 SCM_VALIDATE_THREAD (1, thread);
9de87eea 1392 t = SCM_I_THREAD_DATA (thread);
d823b11b 1393 return t->exited;
5f05c406 1394}
d823b11b 1395#undef FUNC_NAME
5f05c406 1396
9de87eea 1397static scm_i_pthread_cond_t wake_up_cond;
9bc4701c 1398int scm_i_thread_go_to_sleep;
9bc4701c 1399static int threads_initialized_p = 0;
9de87eea 1400static int sleep_level = 0;
9bc4701c
MD
1401
1402void
1403scm_i_thread_put_to_sleep ()
1404{
6087fad9 1405 if (threads_initialized_p)
9bc4701c 1406 {
9de87eea 1407 scm_i_thread *t;
6087fad9 1408
9de87eea
MV
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
76da80e7 1422 {
9de87eea
MV
1423 /* We are already single threaded. Suspend again to update
1424 the recorded stack information.
1425 */
1426 suspend ();
76da80e7 1427 }
9de87eea
MV
1428 sleep_level += 1;
1429
1430 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
9bc4701c
MD
1431 }
1432}
1433
b0dc3d71
MD
1434void
1435scm_i_thread_invalidate_freelists ()
1436{
9de87eea
MV
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;
b0dc3d71
MD
1443}
1444
9bc4701c
MD
1445void
1446scm_i_thread_wake_up ()
1447{
6087fad9 1448 if (threads_initialized_p)
9bc4701c 1449 {
9de87eea
MV
1450 scm_i_thread *t;
1451 scm_i_pthread_mutex_lock (&thread_admin_mutex);
1452
1453 sleep_level -= 1;
1454 if (sleep_level == 0)
76da80e7 1455 {
9de87eea
MV
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);
76da80e7 1459 }
9de87eea
MV
1460
1461 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
1462 scm_enter_guile ((scm_t_guile_ticket) SCM_I_CURRENT_THREAD);
9bc4701c 1463 }
9bc4701c
MD
1464}
1465
1466void
1467scm_i_thread_sleep_for_gc ()
1468{
9de87eea
MV
1469 scm_i_thread *t = suspend ();
1470 scm_i_pthread_cond_wait (&wake_up_cond, &t->heap_mutex);
b0dc3d71 1471 resume (t);
9bc4701c
MD
1472}
1473
9de87eea
MV
1474static void
1475put_to_sleep (void *unused)
1476{
1477 scm_i_thread_put_to_sleep ();
1478}
9bc4701c 1479
9de87eea
MV
1480static void
1481wake_up (void *unused)
1482{
1483 scm_i_thread_wake_up ();
1484}
7bfd3b9e 1485
9de87eea
MV
1486void
1487scm_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
1493scm_i_pthread_mutex_t scm_i_critical_section_mutex =
1494 SCM_I_PTHREAD_MUTEX_INITIALIZER;
a54a94b3 1495
9bc4701c 1496void
9de87eea 1497scm_frame_critical_section ()
76da80e7 1498{
9de87eea
MV
1499 scm_i_frame_pthread_mutex_lock (&scm_i_critical_section_mutex);
1500 scm_frame_block_asyncs ();
1501}
1502
1503/*** Initialization */
1504
1505scm_i_pthread_key_t scm_i_freelist, scm_i_freelist2;
1506scm_i_pthread_mutex_t scm_i_misc_mutex;
1507
1508void
1509scm_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);
9bc4701c
MD
1519}
1520
d823b11b
MV
1521scm_t_bits scm_tc16_thread;
1522scm_t_bits scm_tc16_mutex;
1523scm_t_bits scm_tc16_condvar;
7bfd3b9e 1524
7bfd3b9e 1525void
9de87eea 1526scm_init_threads ()
7bfd3b9e 1527{
9de87eea 1528 scm_tc16_thread = scm_make_smob_type ("thread", sizeof (scm_i_thread));
d823b11b
MV
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
9de87eea
MV
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);
9bc4701c 1537
9de87eea
MV
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);
d823b11b 1543
9de87eea
MV
1544 scm_i_default_dynamic_state = SCM_BOOL_F;
1545 guilify_self_2 (SCM_BOOL_F);
9bc4701c 1546 threads_initialized_p = 1;
7bfd3b9e 1547}
89e00824 1548
5f05c406 1549void
9de87eea 1550scm_init_threads_default_dynamic_state ()
5f05c406 1551{
9de87eea
MV
1552 SCM state = scm_make_dynamic_state (scm_current_dynamic_state ());
1553 scm_i_default_dynamic_state = scm_permanent_object (state);
5f05c406
MV
1554}
1555
d823b11b 1556void
9de87eea 1557scm_init_thread_procs ()
d823b11b 1558{
9de87eea 1559#include "libguile/threads.x"
d823b11b
MV
1560}
1561
89e00824
ML
1562/*
1563 Local Variables:
1564 c-file-style: "gnu"
1565 End:
1566*/