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