* threads.h, async.h, threads.c (SCM_CRITICAL_SECTION_START,
[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
a4d106c7
MV
879void
880scm_frame_lock_mutex (SCM mutex)
881{
882 scm_frame_unwind_handler_with_scm ((void(*)(SCM))scm_unlock_mutex, mutex,
883 SCM_F_WIND_EXPLICITLY);
884 scm_frame_rewind_handler_with_scm ((void(*)(SCM))scm_lock_mutex, mutex,
885 SCM_F_WIND_EXPLICITLY);
886}
887
9de87eea
MV
888static char *
889fat_mutex_trylock (fat_mutex *m, int *resp)
890{
891 char *msg = NULL;
892 SCM thread = scm_current_thread ();
893
894 *resp = 1;
895 scm_i_pthread_mutex_lock (&m->lock);
896 if (scm_is_false (m->owner))
897 m->owner = thread;
898 else if (scm_is_eq (m->owner, thread))
899 {
900 if (m->level >= 0)
901 m->level++;
902 else
903 msg = "mutex already locked by current thread";
904 }
905 else
906 *resp = 0;
907 scm_i_pthread_mutex_unlock (&m->lock);
908 return msg;
909}
910
9bc4701c 911SCM_DEFINE (scm_try_mutex, "try-mutex", 1, 0, 0,
ba1b7223 912 (SCM mutex),
9bc4701c
MD
913"Try to lock @var{mutex}. If the mutex is already locked by someone "
914"else, return @code{#f}. Else lock the mutex and return @code{#t}. ")
915#define FUNC_NAME s_scm_try_mutex
916{
9de87eea
MV
917 char *msg;
918 int res;
919
ba1b7223 920 SCM_VALIDATE_MUTEX (1, mutex);
9bc4701c 921
ba1b7223 922 msg = fat_mutex_trylock (SCM_MUTEX_DATA (mutex), &res);
9de87eea
MV
923 if (msg)
924 scm_misc_error (NULL, msg, SCM_EOL);
925 return scm_from_bool (res);
926}
927#undef FUNC_NAME
76da80e7 928
9de87eea
MV
929static char *
930fat_mutex_unlock (fat_mutex *m)
931{
932 char *msg = NULL;
933
934 scm_i_scm_pthread_mutex_lock (&m->lock);
935 if (!scm_is_eq (m->owner, scm_current_thread ()))
9bc4701c 936 {
9de87eea
MV
937 if (scm_is_false (m->owner))
938 msg = "mutex not locked";
939 else
940 msg = "mutex not locked by current thread";
9bc4701c 941 }
9de87eea
MV
942 else if (m->level > 0)
943 m->level--;
944 else
945 m->owner = unblock_from_queue (m->waiting);
946 scm_i_pthread_mutex_unlock (&m->lock);
947
948 return msg;
9bc4701c 949}
9bc4701c
MD
950
951SCM_DEFINE (scm_unlock_mutex, "unlock-mutex", 1, 0, 0,
952 (SCM mx),
953"Unlocks @var{mutex} if the calling thread owns the lock on "
954"@var{mutex}. Calling unlock-mutex on a mutex not owned by the current "
955"thread results in undefined behaviour. Once a mutex has been unlocked, "
956"one thread blocked on @var{mutex} is awakened and grabs the mutex "
957"lock. Every call to @code{lock-mutex} by this thread must be matched "
958"with a call to @code{unlock-mutex}. Only the last call to "
959"@code{unlock-mutex} will actually unlock the mutex. ")
960#define FUNC_NAME s_scm_unlock_mutex
961{
9de87eea 962 char *msg;
9bc4701c
MD
963 SCM_VALIDATE_MUTEX (1, mx);
964
9de87eea
MV
965 msg = fat_mutex_unlock (SCM_MUTEX_DATA (mx));
966 if (msg)
967 scm_misc_error (NULL, msg, SCM_EOL);
9bc4701c
MD
968 return SCM_BOOL_T;
969}
970#undef FUNC_NAME
971
9de87eea
MV
972#if 0
973
974SCM_DEFINE (scm_mutex_owner, "mutex-owner", 1, 0, 0,
975 (SCM mx),
976 "Return the thread owning @var{mx}, or @code{#f}.")
977#define FUNC_NAME s_scm_mutex_owner
978{
979 SCM_VALIDATE_MUTEX (1, mx);
980 return (SCM_MUTEX_DATA(mx))->owner;
981}
982#undef FUNC_NAME
983
984SCM_DEFINE (scm_mutex_level, "mutex-level", 1, 0, 0,
985 (SCM mx),
986 "Return the lock level of a recursive mutex, or -1\n"
987 "for a standard mutex.")
988#define FUNC_NAME s_scm_mutex_level
989{
990 SCM_VALIDATE_MUTEX (1, mx);
991 return scm_from_int (SCM_MUTEX_DATA(mx)->level);
992}
993#undef FUNC_NAME
994
995#endif
996
997/*** Fat condition variables */
998
999typedef struct {
1000 scm_i_pthread_mutex_t lock;
1001 SCM waiting; /* the threads waiting for this condition. */
1002} fat_cond;
1003
1004#define SCM_CONDVARP(x) SCM_SMOB_PREDICATE (scm_tc16_condvar, x)
1005#define SCM_CONDVAR_DATA(x) ((fat_cond *) SCM_SMOB_DATA (x))
1006
1007static SCM
1008fat_cond_mark (SCM cv)
1009{
1010 fat_cond *c = SCM_CONDVAR_DATA (cv);
1011 return c->waiting;
1012}
1013
1014static size_t
1015fat_cond_free (SCM mx)
1016{
1017 fat_cond *c = SCM_CONDVAR_DATA (mx);
1018 scm_i_pthread_mutex_destroy (&c->lock);
1019 scm_gc_free (c, sizeof (fat_cond), "condition-variable");
1020 return 0;
1021}
1022
1023static int
1024fat_cond_print (SCM cv, SCM port, scm_print_state *pstate SCM_UNUSED)
1025{
1026 fat_cond *c = SCM_CONDVAR_DATA (cv);
1027 scm_puts ("#<condition-variable ", port);
1028 scm_uintprint ((scm_t_bits)c, 16, port);
1029 scm_puts (">", port);
1030 return 1;
1031}
9bc4701c 1032
d823b11b
MV
1033SCM_DEFINE (scm_make_condition_variable, "make-condition-variable", 0, 0, 0,
1034 (void),
1035 "Make a new condition variable.")
1036#define FUNC_NAME s_scm_make_condition_variable
5f05c406 1037{
9de87eea
MV
1038 fat_cond *c;
1039 SCM cv;
1040
1041 c = scm_gc_malloc (sizeof (fat_cond), "condition variable");
1042 scm_i_pthread_mutex_init (&c->lock, 0);
1043 c->waiting = SCM_EOL;
1044 SCM_NEWSMOB (cv, scm_tc16_condvar, (scm_t_bits) c);
1045 c->waiting = make_queue ();
d823b11b 1046 return cv;
5f05c406 1047}
d823b11b 1048#undef FUNC_NAME
5f05c406 1049
9de87eea
MV
1050static int
1051fat_cond_timedwait (SCM cond, SCM mutex,
1052 const scm_t_timespec *waittime)
1053{
1054 scm_i_thread *t = SCM_I_CURRENT_THREAD;
1055 fat_cond *c = SCM_CONDVAR_DATA (cond);
1056 fat_mutex *m = SCM_MUTEX_DATA (mutex);
1057 const char *msg;
1058 int err = 0;
1059
1060 while (1)
1061 {
1062 fprintf (stderr, "cond wait on %p\n", &c->lock);
1063
1064 scm_i_scm_pthread_mutex_lock (&c->lock);
1065 msg = fat_mutex_unlock (m);
1066 t->block_asyncs++;
1067 if (msg == NULL)
1068 {
1069 err = block_self (c->waiting, cond, &c->lock, waittime);
1070 scm_i_pthread_mutex_unlock (&c->lock);
1071 fprintf (stderr, "locking mutex\n");
1072 fat_mutex_lock (mutex);
1073 }
1074 else
1075 scm_i_pthread_mutex_unlock (&c->lock);
1076 t->block_asyncs--;
1077 scm_async_click ();
1078
1079 fprintf (stderr, "back: %s, %d\n", msg, err);
1080
1081 if (msg)
1082 scm_misc_error (NULL, msg, SCM_EOL);
1083
1084 scm_remember_upto_here_2 (cond, mutex);
1085
1086 if (err == 0)
1087 return 1;
1088 if (err == ETIMEDOUT)
1089 return 0;
1090 if (err != EINTR)
1091 {
1092 errno = err;
1093 scm_syserror (NULL);
1094 }
1095 }
1096}
1097
d823b11b
MV
1098SCM_DEFINE (scm_timed_wait_condition_variable, "wait-condition-variable", 2, 1, 0,
1099 (SCM cv, SCM mx, SCM t),
1100"Wait until @var{cond-var} has been signalled. While waiting, "
1101"@var{mutex} is atomically unlocked (as with @code{unlock-mutex}) and "
1102"is locked again when this function returns. When @var{time} is given, "
1103"it specifies a point in time where the waiting should be aborted. It "
1104"can be either a integer as returned by @code{current-time} or a pair "
1105"as returned by @code{gettimeofday}. When the waiting is aborted the "
1106"mutex is locked and @code{#f} is returned. When the condition "
1107"variable is in fact signalled, the mutex is also locked and @code{#t} "
1108"is returned. ")
1109#define FUNC_NAME s_scm_timed_wait_condition_variable
5f05c406 1110{
9de87eea 1111 scm_t_timespec waittime, *waitptr = NULL;
d823b11b
MV
1112
1113 SCM_VALIDATE_CONDVAR (1, cv);
1114 SCM_VALIDATE_MUTEX (2, mx);
9bc4701c 1115
d823b11b
MV
1116 if (!SCM_UNBNDP (t))
1117 {
d2e53ed6 1118 if (scm_is_pair (t))
d823b11b 1119 {
9de87eea
MV
1120 waittime.tv_sec = scm_to_ulong (SCM_CAR (t));
1121 waittime.tv_nsec = scm_to_ulong (SCM_CAR (t)) * 1000;
d823b11b
MV
1122 }
1123 else
1124 {
9de87eea 1125 waittime.tv_sec = scm_to_ulong (t);
d823b11b
MV
1126 waittime.tv_nsec = 0;
1127 }
9de87eea 1128 waitptr = &waittime;
d823b11b
MV
1129 }
1130
9de87eea 1131 return scm_from_bool (fat_cond_timedwait (cv, mx, waitptr));
5f05c406 1132}
d823b11b 1133#undef FUNC_NAME
5f05c406 1134
9de87eea
MV
1135static void
1136fat_cond_signal (fat_cond *c)
1137{
1138 fprintf (stderr, "cond signal on %p\n", &c->lock);
1139
1140 scm_i_scm_pthread_mutex_lock (&c->lock);
1141 unblock_from_queue (c->waiting);
1142 scm_i_pthread_mutex_unlock (&c->lock);
1143}
1144
d823b11b
MV
1145SCM_DEFINE (scm_signal_condition_variable, "signal-condition-variable", 1, 0, 0,
1146 (SCM cv),
1147 "Wake up one thread that is waiting for @var{cv}")
1148#define FUNC_NAME s_scm_signal_condition_variable
5f05c406 1149{
d823b11b 1150 SCM_VALIDATE_CONDVAR (1, cv);
9de87eea 1151 fat_cond_signal (SCM_CONDVAR_DATA (cv));
d823b11b 1152 return SCM_BOOL_T;
5f05c406 1153}
d823b11b 1154#undef FUNC_NAME
5f05c406 1155
9de87eea
MV
1156static void
1157fat_cond_broadcast (fat_cond *c)
1158{
1159 scm_i_scm_pthread_mutex_lock (&c->lock);
1160 while (scm_is_true (unblock_from_queue (c->waiting)))
1161 ;
1162 scm_i_pthread_mutex_unlock (&c->lock);
1163}
1164
d823b11b
MV
1165SCM_DEFINE (scm_broadcast_condition_variable, "broadcast-condition-variable", 1, 0, 0,
1166 (SCM cv),
1167 "Wake up all threads that are waiting for @var{cv}. ")
1168#define FUNC_NAME s_scm_broadcast_condition_variable
5f05c406 1169{
d823b11b 1170 SCM_VALIDATE_CONDVAR (1, cv);
9de87eea 1171 fat_cond_broadcast (SCM_CONDVAR_DATA (cv));
d823b11b 1172 return SCM_BOOL_T;
5f05c406 1173}
d823b11b 1174#undef FUNC_NAME
5f05c406 1175
d823b11b
MV
1176/*** Marking stacks */
1177
1178/* XXX - what to do with this? Do we need to handle this for blocked
1179 threads as well?
1180*/
1181#ifdef __ia64__
1182# define SCM_MARK_BACKING_STORE() do { \
1183 ucontext_t ctx; \
1184 SCM_STACKITEM * top, * bot; \
1185 getcontext (&ctx); \
1186 scm_mark_locations ((SCM_STACKITEM *) &ctx.uc_mcontext, \
1187 ((size_t) (sizeof (SCM_STACKITEM) - 1 + sizeof ctx.uc_mcontext) \
1188 / sizeof (SCM_STACKITEM))); \
1189 bot = (SCM_STACKITEM *) __libc_ia64_register_backing_store_base; \
1190 top = (SCM_STACKITEM *) ctx.uc_mcontext.sc_ar_bsp; \
1191 scm_mark_locations (bot, top - bot); } while (0)
1192#else
1193# define SCM_MARK_BACKING_STORE()
1194#endif
1195
1196void
1197scm_threads_mark_stacks (void)
5f05c406 1198{
9de87eea
MV
1199 scm_i_thread *t;
1200 for (t = all_threads; t; t = t->next_thread)
d823b11b 1201 {
9de87eea
MV
1202 /* Check that thread has indeed been suspended.
1203 */
1204 assert (t->top);
6087fad9 1205
9de87eea 1206 scm_gc_mark (t->handle);
6087fad9 1207
d028af45 1208#if SCM_STACK_GROWS_UP
9de87eea 1209 scm_mark_locations (t->base, t->top - t->base);
d823b11b 1210#else
9de87eea 1211 scm_mark_locations (t->top, t->base - t->top);
d823b11b 1212#endif
6087fad9
MV
1213 scm_mark_locations ((SCM_STACKITEM *) t->regs,
1214 ((size_t) sizeof(t->regs)
1215 / sizeof (SCM_STACKITEM)));
d823b11b 1216 }
59152722
MV
1217
1218 SCM_MARK_BACKING_STORE ();
5f05c406
MV
1219}
1220
d823b11b
MV
1221/*** Select */
1222
911782b7 1223int
9de87eea
MV
1224scm_std_select (int nfds,
1225 SELECT_TYPE *readfds,
1226 SELECT_TYPE *writefds,
1227 SELECT_TYPE *exceptfds,
1228 struct timeval *timeout)
1229{
1230 fd_set my_readfds;
1231 int res, eno, wakeup_fd;
1232 scm_i_thread *t = SCM_I_CURRENT_THREAD;
1233 scm_t_guile_ticket ticket;
1234
1235 if (readfds == NULL)
1236 {
1237 FD_ZERO (&my_readfds);
1238 readfds = &my_readfds;
1239 }
1240
1241 while (scm_i_setup_sleep (t, SCM_BOOL_F, NULL, t->sleep_pipe[1]))
1242 SCM_TICK;
1243
1244 wakeup_fd = t->sleep_pipe[0];
1245 ticket = scm_leave_guile ();
1246 FD_SET (wakeup_fd, readfds);
1247 if (wakeup_fd >= nfds)
1248 nfds = wakeup_fd+1;
1249 res = select (nfds, readfds, writefds, exceptfds, timeout);
1250 t->sleep_fd = -1;
d823b11b 1251 eno = errno;
9de87eea
MV
1252 scm_enter_guile (ticket);
1253
1254 scm_i_reset_sleep (t);
1255
1256 if (res > 0 && FD_ISSET (wakeup_fd, readfds))
1257 {
1258 char dummy;
1259 read (wakeup_fd, &dummy, 1);
1260 FD_CLR (wakeup_fd, readfds);
1261 res -= 1;
1262 if (res == 0)
1263 {
1264 eno = EINTR;
1265 res = -1;
1266 }
1267 }
d823b11b
MV
1268 errno = eno;
1269 return res;
5f05c406
MV
1270}
1271
9de87eea 1272/* Convenience API for blocking while in guile mode. */
76da80e7 1273
9de87eea 1274#if SCM_USE_PTHREAD_THREADS
92e64b87 1275
9bc4701c 1276int
9de87eea 1277scm_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
9bc4701c 1278{
9de87eea
MV
1279 scm_t_guile_ticket t = scm_leave_guile ();
1280 int res = scm_i_pthread_mutex_lock (mutex);
1281 scm_enter_guile (t);
9bc4701c
MD
1282 return res;
1283}
1284
9de87eea
MV
1285static void
1286unlock (void *data)
28d52ebb 1287{
9de87eea 1288 scm_i_pthread_mutex_unlock ((scm_i_pthread_mutex_t *)data);
28d52ebb
MD
1289}
1290
1291void
9de87eea 1292scm_frame_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
28d52ebb 1293{
9de87eea
MV
1294 scm_i_scm_pthread_mutex_lock (mutex);
1295 scm_frame_unwind_handler (unlock, mutex, SCM_F_WIND_EXPLICITLY);
28d52ebb
MD
1296}
1297
9bc4701c 1298int
9de87eea 1299scm_pthread_cond_wait (scm_i_pthread_cond_t *cond, scm_i_pthread_mutex_t *mutex)
9bc4701c 1300{
9de87eea
MV
1301 scm_t_guile_ticket t = scm_leave_guile ();
1302 int res = scm_i_pthread_cond_wait (cond, mutex);
1303 scm_enter_guile (t);
9bc4701c
MD
1304 return res;
1305}
9bc4701c 1306
76da80e7 1307int
9de87eea
MV
1308scm_pthread_cond_timedwait (scm_i_pthread_cond_t *cond,
1309 scm_i_pthread_mutex_t *mutex,
1310 const scm_t_timespec *wt)
76da80e7 1311{
9de87eea
MV
1312 scm_t_guile_ticket t = scm_leave_guile ();
1313 int res = scm_i_pthread_cond_timedwait (cond, mutex, wt);
1314 scm_enter_guile (t);
1315 return res;
76da80e7
MV
1316}
1317
9de87eea 1318#endif
76da80e7 1319
d823b11b 1320unsigned long
9de87eea 1321scm_std_usleep (unsigned long usecs)
5f05c406 1322{
d823b11b
MV
1323 struct timeval tv;
1324 tv.tv_usec = usecs % 1000000;
1325 tv.tv_sec = usecs / 1000000;
9de87eea
MV
1326 scm_std_select (0, NULL, NULL, NULL, &tv);
1327 return tv.tv_sec * 1000000 + tv.tv_usec;
5f05c406
MV
1328}
1329
9de87eea
MV
1330unsigned int
1331scm_std_sleep (unsigned int secs)
6c214b62 1332{
d823b11b
MV
1333 struct timeval tv;
1334 tv.tv_usec = 0;
1335 tv.tv_sec = secs;
9de87eea 1336 scm_std_select (0, NULL, NULL, NULL, &tv);
d823b11b 1337 return tv.tv_sec;
6c214b62
MD
1338}
1339
d823b11b
MV
1340/*** Misc */
1341
1342SCM_DEFINE (scm_current_thread, "current-thread", 0, 0, 0,
1343 (void),
1344 "Return the thread that called this function.")
1345#define FUNC_NAME s_scm_current_thread
1346{
9de87eea 1347 return SCM_I_CURRENT_THREAD->handle;
d823b11b
MV
1348}
1349#undef FUNC_NAME
1350
9de87eea
MV
1351static SCM
1352scm_c_make_list (size_t n, SCM fill)
1353{
1354 SCM res = SCM_EOL;
1355 while (n-- > 0)
1356 res = scm_cons (fill, res);
1357 return res;
1358}
1359
d823b11b
MV
1360SCM_DEFINE (scm_all_threads, "all-threads", 0, 0, 0,
1361 (void),
1362 "Return a list of all threads.")
9bc4701c 1363#define FUNC_NAME s_scm_all_threads
d823b11b 1364{
9de87eea
MV
1365 /* We can not allocate while holding the thread_admin_mutex because
1366 of the way GC is done.
1367 */
1368 int n = thread_count;
1369 scm_i_thread *t;
1370 SCM list = scm_c_make_list (n, SCM_UNSPECIFIED), *l;
d823b11b 1371
9de87eea
MV
1372 scm_i_pthread_mutex_lock (&thread_admin_mutex);
1373 l = &list;
1374 for (t = all_threads; t && n > 0; t = t->next_thread)
1375 {
1376 SCM_SETCAR (*l, t->handle);
1377 l = SCM_CDRLOC (*l);
1378 n--;
1379 }
1380 *l = SCM_EOL;
1381 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
1382 return list;
d823b11b 1383}
9de87eea 1384#undef FUNC_NAME
d823b11b
MV
1385
1386SCM_DEFINE (scm_thread_exited_p, "thread-exited?", 1, 0, 0,
1387 (SCM thread),
1388 "Return @code{#t} iff @var{thread} has exited.\n")
1389#define FUNC_NAME s_scm_thread_exited_p
1390{
7888309b 1391 return scm_from_bool (scm_c_thread_exited_p (thread));
d823b11b
MV
1392}
1393#undef FUNC_NAME
1394
911782b7 1395int
d823b11b
MV
1396scm_c_thread_exited_p (SCM thread)
1397#define FUNC_NAME s_scm_thread_exited_p
5f05c406 1398{
9de87eea 1399 scm_i_thread *t;
d823b11b 1400 SCM_VALIDATE_THREAD (1, thread);
9de87eea 1401 t = SCM_I_THREAD_DATA (thread);
d823b11b 1402 return t->exited;
5f05c406 1403}
d823b11b 1404#undef FUNC_NAME
5f05c406 1405
9de87eea 1406static scm_i_pthread_cond_t wake_up_cond;
9bc4701c 1407int scm_i_thread_go_to_sleep;
9bc4701c 1408static int threads_initialized_p = 0;
9de87eea 1409static int sleep_level = 0;
9bc4701c
MD
1410
1411void
1412scm_i_thread_put_to_sleep ()
1413{
6087fad9 1414 if (threads_initialized_p)
9bc4701c 1415 {
9de87eea 1416 scm_i_thread *t;
6087fad9 1417
9de87eea
MV
1418 scm_leave_guile ();
1419 scm_i_pthread_mutex_lock (&thread_admin_mutex);
1420
1421 if (sleep_level == 0)
1422 {
1423 /* Signal all threads to go to sleep
1424 */
1425 scm_i_thread_go_to_sleep = 1;
1426 for (t = all_threads; t; t = t->next_thread)
1427 scm_i_pthread_mutex_lock (&t->heap_mutex);
1428 scm_i_thread_go_to_sleep = 0;
1429 }
1430 else
76da80e7 1431 {
9de87eea
MV
1432 /* We are already single threaded. Suspend again to update
1433 the recorded stack information.
1434 */
1435 suspend ();
76da80e7 1436 }
9de87eea
MV
1437 sleep_level += 1;
1438
1439 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
9bc4701c
MD
1440 }
1441}
1442
b0dc3d71
MD
1443void
1444scm_i_thread_invalidate_freelists ()
1445{
9de87eea
MV
1446 /* thread_admin_mutex is already locked. */
1447
1448 scm_i_thread *t;
1449 for (t = all_threads; t; t = t->next_thread)
1450 if (t != SCM_I_CURRENT_THREAD)
1451 t->clear_freelists_p = 1;
b0dc3d71
MD
1452}
1453
9bc4701c
MD
1454void
1455scm_i_thread_wake_up ()
1456{
6087fad9 1457 if (threads_initialized_p)
9bc4701c 1458 {
9de87eea
MV
1459 scm_i_thread *t;
1460 scm_i_pthread_mutex_lock (&thread_admin_mutex);
1461
1462 sleep_level -= 1;
1463 if (sleep_level == 0)
76da80e7 1464 {
9de87eea
MV
1465 scm_i_pthread_cond_broadcast (&wake_up_cond);
1466 for (t = all_threads; t; t = t->next_thread)
1467 scm_i_pthread_mutex_unlock (&t->heap_mutex);
76da80e7 1468 }
9de87eea
MV
1469
1470 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
1471 scm_enter_guile ((scm_t_guile_ticket) SCM_I_CURRENT_THREAD);
9bc4701c 1472 }
9bc4701c
MD
1473}
1474
1475void
1476scm_i_thread_sleep_for_gc ()
1477{
9de87eea
MV
1478 scm_i_thread *t = suspend ();
1479 scm_i_pthread_cond_wait (&wake_up_cond, &t->heap_mutex);
b0dc3d71 1480 resume (t);
9bc4701c
MD
1481}
1482
9de87eea
MV
1483static void
1484put_to_sleep (void *unused)
1485{
1486 scm_i_thread_put_to_sleep ();
1487}
9bc4701c 1488
9de87eea
MV
1489static void
1490wake_up (void *unused)
1491{
1492 scm_i_thread_wake_up ();
1493}
7bfd3b9e 1494
9de87eea
MV
1495void
1496scm_i_frame_single_threaded ()
1497{
1498 scm_frame_rewind_handler (put_to_sleep, NULL, SCM_F_WIND_EXPLICITLY);
1499 scm_frame_unwind_handler (wake_up, NULL, SCM_F_WIND_EXPLICITLY);
1500}
1501
a4d106c7
MV
1502/* This mutex is used by SCM_CRITICAL_SECTION_START/END.
1503 */
9de87eea 1504scm_i_pthread_mutex_t scm_i_critical_section_mutex =
a4d106c7
MV
1505 SCM_I_PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
1506int scm_i_critical_section_level = 0;
1507
1508static SCM framed_critical_section_mutex;
a54a94b3 1509
9bc4701c 1510void
a4d106c7 1511scm_frame_critical_section (SCM mutex)
76da80e7 1512{
a4d106c7
MV
1513 if (scm_is_false (mutex))
1514 mutex = framed_critical_section_mutex;
1515 scm_frame_lock_mutex (mutex);
9de87eea
MV
1516 scm_frame_block_asyncs ();
1517}
1518
1519/*** Initialization */
1520
1521scm_i_pthread_key_t scm_i_freelist, scm_i_freelist2;
1522scm_i_pthread_mutex_t scm_i_misc_mutex;
1523
1524void
1525scm_threads_prehistory (SCM_STACKITEM *base)
1526{
9de87eea
MV
1527 scm_i_pthread_mutex_init (&scm_i_misc_mutex, NULL);
1528 scm_i_pthread_cond_init (&wake_up_cond, NULL);
9de87eea
MV
1529 scm_i_pthread_key_create (&scm_i_freelist, NULL);
1530 scm_i_pthread_key_create (&scm_i_freelist2, NULL);
1531
1532 guilify_self_1 (base);
9bc4701c
MD
1533}
1534
d823b11b
MV
1535scm_t_bits scm_tc16_thread;
1536scm_t_bits scm_tc16_mutex;
1537scm_t_bits scm_tc16_condvar;
7bfd3b9e 1538
7bfd3b9e 1539void
9de87eea 1540scm_init_threads ()
7bfd3b9e 1541{
9de87eea 1542 scm_tc16_thread = scm_make_smob_type ("thread", sizeof (scm_i_thread));
d823b11b
MV
1543 scm_set_smob_mark (scm_tc16_thread, thread_mark);
1544 scm_set_smob_print (scm_tc16_thread, thread_print);
1545 scm_set_smob_free (scm_tc16_thread, thread_free);
1546
9de87eea
MV
1547 scm_tc16_mutex = scm_make_smob_type ("mutex", sizeof (fat_mutex));
1548 scm_set_smob_mark (scm_tc16_mutex, fat_mutex_mark);
1549 scm_set_smob_print (scm_tc16_mutex, fat_mutex_print);
1550 scm_set_smob_free (scm_tc16_mutex, fat_mutex_free);
9bc4701c 1551
9de87eea
MV
1552 scm_tc16_condvar = scm_make_smob_type ("condition-variable",
1553 sizeof (fat_cond));
1554 scm_set_smob_mark (scm_tc16_condvar, fat_cond_mark);
1555 scm_set_smob_print (scm_tc16_condvar, fat_cond_print);
1556 scm_set_smob_free (scm_tc16_condvar, fat_cond_free);
d823b11b 1557
9de87eea
MV
1558 scm_i_default_dynamic_state = SCM_BOOL_F;
1559 guilify_self_2 (SCM_BOOL_F);
9bc4701c 1560 threads_initialized_p = 1;
a4d106c7
MV
1561
1562 framed_critical_section_mutex =
1563 scm_permanent_object (scm_make_recursive_mutex ());
7bfd3b9e 1564}
89e00824 1565
5f05c406 1566void
9de87eea 1567scm_init_threads_default_dynamic_state ()
5f05c406 1568{
9de87eea
MV
1569 SCM state = scm_make_dynamic_state (scm_current_dynamic_state ());
1570 scm_i_default_dynamic_state = scm_permanent_object (state);
5f05c406
MV
1571}
1572
d823b11b 1573void
9de87eea 1574scm_init_thread_procs ()
d823b11b 1575{
9de87eea 1576#include "libguile/threads.x"
d823b11b
MV
1577}
1578
89e00824
ML
1579/*
1580 Local Variables:
1581 c-file-style: "gnu"
1582 End:
1583*/