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