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