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