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