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