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