* threads.c (scm_thread): Removed filed joining_threads.
[bpt/guile.git] / libguile / threads.c
1 /* Copyright (C) 1995,1996,1997,1998,2000,2001, 2002 Free Software Foundation, Inc.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * This program 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
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
17 *
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
20 *
21 * The exception is that, if you link the GUILE library with other files
22 * to produce an executable, this does not by itself cause the
23 * resulting executable to be covered by the GNU General Public License.
24 * Your use of that executable is in no way restricted on account of
25 * linking the GUILE library code into it.
26 *
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
29 *
30 * This exception applies only to the code released by the
31 * Free Software Foundation under the name GUILE. If you copy
32 * code from other Free Software Foundation releases into a copy of
33 * GUILE, as the General Public License permits, the exception does
34 * not apply to the code that you add in this way. To avoid misleading
35 * anyone as to the status of such modified files, you must delete
36 * this exception notice from them.
37 *
38 * If you write modifications of your own for GUILE, it is your choice
39 * whether to permit this exception to apply to your modifications.
40 * If you do not wish that, delete this exception notice. */
41
42
43 \f
44
45 /* This file implements nice Scheme level threads on top of the gastly
46 C level threads.
47 */
48
49 #include <unistd.h>
50 #include <stdio.h>
51 #include <assert.h>
52 #include <sys/time.h>
53
54 #include "libguile/_scm.h"
55 #include "libguile/validate.h"
56 #include "libguile/root.h"
57 #include "libguile/eval.h"
58 #include "libguile/async.h"
59 #include "libguile/ports.h"
60 #include "libguile/threads.h"
61 #include "libguile/dynwind.h"
62 #include "libguile/iselect.h"
63
64 /*** Queues */
65
66 static SCM
67 make_queue ()
68 {
69 return scm_cons (SCM_EOL, SCM_EOL);
70 }
71
72 static SCM
73 enqueue (SCM q, SCM t)
74 {
75 SCM c = scm_cons (t, SCM_EOL);
76 if (SCM_NULLP (SCM_CDR (q)))
77 SCM_SETCDR (q, c);
78 else
79 SCM_SETCDR (SCM_CAR (q), c);
80 SCM_SETCAR (q, c);
81 return c;
82 }
83
84 static void
85 remqueue (SCM q, SCM c)
86 {
87 SCM p, prev = q;
88 for (p = SCM_CDR (q); !SCM_NULLP (p); p = SCM_CDR (p))
89 {
90 if (SCM_EQ_P (p, c))
91 {
92 if (SCM_EQ_P (c, SCM_CAR (q)))
93 SCM_SETCAR (q, SCM_CDR (c));
94 SCM_SETCDR (prev, SCM_CDR (c));
95 return;
96 }
97 prev = p;
98 }
99 abort ();
100 }
101
102 static SCM
103 dequeue (SCM q)
104 {
105 SCM c = SCM_CDR (q);
106 if (SCM_NULLP (c))
107 return SCM_BOOL_F;
108 else
109 {
110 SCM_SETCDR (q, SCM_CDR (c));
111 if (SCM_NULLP (SCM_CDR (q)))
112 SCM_SETCAR (q, SCM_EOL);
113 return SCM_CAR (c);
114 }
115 }
116
117 /*** Threads */
118
119 #define THREAD_INITIALIZED_P(t) (t->base != NULL)
120
121 struct scm_thread {
122
123 /* Blocking.
124 */
125 scm_t_cond sleep_cond;
126 struct scm_thread *next_waiting;
127
128 /* This mutex represents this threads right to access the heap.
129 That right can temporarily be taken away by the GC. */
130 scm_t_mutex heap_mutex;
131 int clear_freelists_p; /* set if GC was done while thread was asleep */
132
133 scm_root_state *root;
134 SCM handle;
135 scm_t_thread thread;
136 SCM result;
137 int exited;
138
139 /* For keeping track of the stack and registers. */
140 SCM_STACKITEM *base;
141 SCM_STACKITEM *top;
142 jmp_buf regs;
143
144 };
145
146 static SCM
147 make_thread (SCM creation_protects)
148 {
149 SCM z;
150 scm_thread *t;
151 z = scm_make_smob (scm_tc16_thread);
152 t = SCM_THREAD_DATA (z);
153 t->handle = z;
154 t->result = creation_protects;
155 t->base = NULL;
156 scm_i_plugin_cond_init (&t->sleep_cond, 0);
157 scm_i_plugin_mutex_init (&t->heap_mutex, &scm_i_plugin_mutex);
158 t->clear_freelists_p = 0;
159 t->exited = 0;
160 return z;
161 }
162
163 static void
164 init_thread_creatant (SCM thread,
165 SCM_STACKITEM *base)
166 {
167 scm_thread *t = SCM_THREAD_DATA (thread);
168 t->thread = scm_thread_self ();
169 t->base = base;
170 t->top = NULL;
171 }
172
173 static SCM
174 thread_mark (SCM obj)
175 {
176 scm_thread *t = SCM_THREAD_DATA (obj);
177 scm_gc_mark (t->result);
178 return t->root->handle; /* mark root-state of this thread */
179 }
180
181 static int
182 thread_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
183 {
184 scm_thread *t = SCM_THREAD_DATA (exp);
185 scm_puts ("#<thread ", port);
186 scm_intprint ((unsigned long)t->thread, 10, port);
187 scm_puts (" (", port);
188 scm_intprint ((unsigned long)t, 16, port);
189 scm_puts (")>", port);
190 return 1;
191 }
192
193 static size_t
194 thread_free (SCM obj)
195 {
196 scm_thread *t = SCM_THREAD_DATA (obj);
197 if (!t->exited)
198 abort ();
199 scm_gc_free (t, sizeof (*t), "thread");
200 return 0;
201 }
202
203 /*** Scheduling */
204
205 #define cur_thread (SCM_CURRENT_THREAD->handle)
206 scm_t_key scm_i_thread_key;
207 scm_t_key scm_i_root_state_key;
208
209 void
210 scm_i_set_thread_data (void *data)
211 {
212 scm_thread *t = SCM_CURRENT_THREAD;
213 scm_setspecific (scm_i_root_state_key, data);
214 t->root = (scm_root_state *)data;
215 }
216
217 static void
218 resume (scm_thread *t)
219 {
220 t->top = NULL;
221 if (t->clear_freelists_p)
222 {
223 *SCM_FREELIST_LOC (scm_i_freelist) = SCM_EOL;
224 *SCM_FREELIST_LOC (scm_i_freelist2) = SCM_EOL;
225 t->clear_freelists_p = 0;
226 }
227 }
228
229 void
230 scm_i_enter_guile (scm_thread *t)
231 {
232 scm_i_plugin_mutex_lock (&t->heap_mutex);
233 resume (t);
234 }
235
236 static scm_thread *
237 suspend ()
238 {
239 scm_thread *c = SCM_CURRENT_THREAD;
240
241 /* record top of stack for the GC */
242 c->top = (SCM_STACKITEM *)&c;
243 /* save registers. */
244 SCM_FLUSH_REGISTER_WINDOWS;
245 setjmp (c->regs);
246
247 return c;
248 }
249
250 scm_thread *
251 scm_i_leave_guile ()
252 {
253 scm_thread *t = suspend ();
254 scm_i_plugin_mutex_unlock (&t->heap_mutex);
255 return t;
256 }
257
258 /* Put the current thread to sleep until it is explicitely unblocked.
259 */
260 static int
261 block ()
262 {
263 int err;
264 scm_thread *t = suspend ();
265 err = scm_i_plugin_cond_wait (&t->sleep_cond, &t->heap_mutex);
266 resume (t);
267 return err;
268 }
269
270 /* Put the current thread to sleep until it is explicitely unblocked
271 or until a signal arrives or until time AT (absolute time) is
272 reached. Return 0 when it has been unblocked; errno otherwise.
273 */
274 static int
275 timed_block (const struct timespec *at)
276 {
277 int err;
278 scm_thread *t = suspend ();
279 err = scm_i_plugin_cond_timedwait (&t->sleep_cond, &t->heap_mutex, at);
280 resume (t);
281 return err;
282 }
283
284 /* Unblock a sleeping thread.
285 */
286 static void
287 unblock (scm_thread *t)
288 {
289 scm_i_plugin_cond_signal (&t->sleep_cond);
290 }
291
292 /*** Thread creation */
293
294 static scm_t_mutex thread_admin_mutex;
295 static SCM all_threads;
296 static int thread_count;
297
298 typedef struct launch_data {
299 SCM thread;
300 SCM rootcont;
301 scm_t_catch_body body;
302 void *body_data;
303 scm_t_catch_handler handler;
304 void *handler_data;
305 } launch_data;
306
307 static SCM
308 body_bootstrip (launch_data* data)
309 {
310 /* First save the new root continuation */
311 data->rootcont = scm_root->rootcont;
312 return (data->body) (data->body_data);
313 }
314
315 static SCM
316 handler_bootstrip (launch_data* data, SCM tag, SCM throw_args)
317 {
318 scm_root->rootcont = data->rootcont;
319 return (data->handler) (data->handler_data, tag, throw_args);
320 }
321
322 static void
323 really_launch (SCM_STACKITEM *base, launch_data *data)
324 {
325 SCM thread;
326 scm_thread *t;
327 thread = data->thread;
328 t = SCM_THREAD_DATA (thread);
329 SCM_FREELIST_CREATE (scm_i_freelist);
330 SCM_FREELIST_CREATE (scm_i_freelist2);
331 scm_setspecific (scm_i_thread_key, t);
332 scm_setspecific (scm_i_root_state_key, t->root);
333 scm_i_plugin_mutex_lock (&t->heap_mutex); /* ensure that we "own" the heap */
334 init_thread_creatant (thread, base); /* must own the heap */
335
336 data->rootcont = SCM_BOOL_F;
337 t->result =
338 scm_internal_cwdr ((scm_t_catch_body) body_bootstrip,
339 data,
340 (scm_t_catch_handler) handler_bootstrip,
341 data, base);
342 scm_i_leave_guile (); /* release the heap */
343 free (data);
344
345 scm_i_plugin_mutex_lock (&thread_admin_mutex);
346 all_threads = scm_delq_x (thread, all_threads);
347 t->exited = 1;
348 thread_count--;
349 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
350
351 scm_thread_detach (t->thread);
352 }
353
354 static void *
355 launch_thread (void *p)
356 {
357 really_launch ((SCM_STACKITEM *)&p, (launch_data *)p);
358 return 0;
359 }
360
361 static SCM
362 create_thread (scm_t_catch_body body, void *body_data,
363 scm_t_catch_handler handler, void *handler_data,
364 SCM protects)
365 {
366 SCM thread;
367
368 /* Make new thread. The first thing the new thread will do is to
369 lock guile_mutex. Thus, we can safely complete its
370 initialization after creating it. While the new thread starts,
371 all its data is protected via all_threads.
372 */
373
374 {
375 scm_t_thread th;
376 SCM root, old_winds;
377 launch_data *data;
378 scm_thread *t;
379 int err;
380
381 /* Unwind wind chain. */
382 old_winds = scm_dynwinds;
383 scm_dowinds (SCM_EOL, scm_ilength (scm_root->dynwinds));
384
385 /* Allocate thread locals. */
386 root = scm_make_root (scm_root->handle);
387 data = scm_malloc (sizeof (launch_data));
388
389 /* Make thread. */
390 thread = make_thread (protects);
391 data->thread = thread;
392 data->body = body;
393 data->body_data = body_data;
394 data->handler = handler;
395 data->handler_data = handler_data;
396 t = SCM_THREAD_DATA (thread);
397 /* must initialize root state pointer before the thread is linked
398 into all_threads */
399 t->root = SCM_ROOT_STATE (root);
400
401 /* In order to avoid the need of synchronization between parent
402 and child thread, we need to insert the child into all_threads
403 before creation. */
404 {
405 SCM new_threads = scm_cons (thread, SCM_BOOL_F); /* could cause GC */
406 scm_thread *parent = scm_i_leave_guile (); /* to prevent deadlock */
407 scm_i_plugin_mutex_lock (&thread_admin_mutex);
408 SCM_SETCDR (new_threads, all_threads);
409 all_threads = new_threads;
410 thread_count++;
411 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
412
413 scm_remember_upto_here_1 (root);
414
415 scm_i_enter_guile (parent);
416 }
417
418 err = scm_i_plugin_thread_create (&th, 0, launch_thread, (void *) data);
419 if (err != 0)
420 {
421 scm_i_plugin_mutex_lock (&thread_admin_mutex);
422 all_threads = scm_delq_x (thread, all_threads);
423 ((scm_thread *) SCM_THREAD_DATA(thread))->exited = 1;
424 thread_count--;
425 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
426 }
427
428 /* Return to old dynamic context. */
429 scm_dowinds (old_winds, - scm_ilength (old_winds));
430
431 if (err)
432 {
433 errno = err;
434 scm_syserror ("create-thread");
435 }
436 }
437
438 return thread;
439 }
440
441 SCM_DEFINE (scm_call_with_new_thread, "call-with-new-thread", 2, 0, 0,
442 (SCM thunk, SCM handler),
443 "Evaluate @var{(thunk)} in a new thread, and new dynamic context, "
444 "returning a new thread object representing the thread. "
445 "If an error occurs during evaluation, call error-thunk, passing it an "
446 "error code describing the condition. "
447 "If this happens, the error-thunk is called outside the scope of the new "
448 "root -- it is called in the same dynamic context in which "
449 "with-new-thread was evaluated, but not in the callers thread. "
450 "All the evaluation rules for dynamic roots apply to threads.")
451 #define FUNC_NAME s_scm_call_with_new_thread
452 {
453 SCM_ASSERT (SCM_NFALSEP (scm_thunk_p (thunk)), thunk, SCM_ARG1, FUNC_NAME);
454 SCM_ASSERT (SCM_NFALSEP (scm_procedure_p (handler)), handler, SCM_ARG2,
455 FUNC_NAME);
456
457 return create_thread ((scm_t_catch_body) scm_call_0, thunk,
458 (scm_t_catch_handler) scm_apply_1, handler,
459 scm_cons (thunk, handler));
460 }
461 #undef FUNC_NAME
462
463 SCM_DEFINE (scm_join_thread, "join-thread", 1, 0, 0,
464 (SCM thread),
465 "Suspend execution of the calling thread until the target @var{thread} "
466 "terminates, unless the target @var{thread} has already terminated. ")
467 #define FUNC_NAME s_scm_join_thread
468 {
469 scm_thread *t;
470 SCM res;
471
472 SCM_VALIDATE_THREAD (1, thread);
473 if (SCM_EQ_P (cur_thread, thread))
474 SCM_MISC_ERROR ("can not join the current thread", SCM_EOL);
475
476 t = SCM_THREAD_DATA (thread);
477 if (!t->exited)
478 {
479 scm_thread *c = scm_i_leave_guile ();
480 while (!THREAD_INITIALIZED_P (t))
481 SCM_TICK;
482 scm_thread_join (t->thread, 0);
483 scm_i_enter_guile (c);
484 }
485 res = t->result;
486 t->result = SCM_BOOL_F;
487 return res;
488 }
489 #undef FUNC_NAME
490
491 SCM *scm_loc_sys_thread_handler;
492
493 SCM
494 scm_i_make_future (SCM thunk)
495 {
496 SCM_RETURN_NEWSMOB2 (scm_tc16_future,
497 create_thread ((scm_t_catch_body) scm_call_0,
498 thunk,
499 (scm_t_catch_handler) scm_apply_1,
500 *scm_loc_sys_thread_handler,
501 scm_cons (thunk,
502 *scm_loc_sys_thread_handler)),
503 scm_make_rec_mutex ());
504 }
505
506 static size_t
507 future_free (SCM future)
508 {
509 scm_rec_mutex_free (SCM_FUTURE_MUTEX (future));
510 return 0;
511 }
512
513 static int
514 future_print (SCM exp, SCM port, scm_print_state *pstate)
515 {
516 int writingp = SCM_WRITINGP (pstate);
517 scm_puts ("#<future ", port);
518 SCM_SET_WRITINGP (pstate, 1);
519 scm_iprin1 (SCM_FUTURE_DATA (exp), port, pstate);
520 SCM_SET_WRITINGP (pstate, writingp);
521 scm_putc ('>', port);
522 return !0;
523 }
524
525 SCM_DEFINE (scm_future_ref, "future-ref", 1, 0, 0,
526 (SCM future),
527 "If the future @var{x} has not been computed yet, compute and\n"
528 "return @var{x}, otherwise just return the previously computed\n"
529 "value.")
530 #define FUNC_NAME s_scm_future_ref
531 {
532 SCM_VALIDATE_FUTURE (1, future);
533 scm_rec_mutex_lock (SCM_FUTURE_MUTEX (future));
534 if (!SCM_FUTURE_COMPUTED_P (future))
535 {
536 SCM value = scm_join_thread (SCM_FUTURE_DATA (future));
537 if (!SCM_FUTURE_COMPUTED_P (future))
538 {
539 SCM_SET_FUTURE_DATA (future, value);
540 SCM_SET_FUTURE_COMPUTED (future);
541 }
542 }
543 scm_rec_mutex_unlock (SCM_FUTURE_MUTEX (future));
544 return SCM_FUTURE_DATA (future);
545 }
546 #undef FUNC_NAME
547
548 /*** Fair mutexes */
549
550 /* We implement our own mutex type since we want them to be 'fair', we
551 want to do fancy things while waiting for them (like running
552 asyncs) and we want to support waiting on many things at once.
553 Also, we might add things that are nice for debugging.
554 */
555
556 typedef struct fair_mutex {
557 /* the thread currently owning the mutex, or SCM_BOOL_F. */
558 scm_t_mutex lock;
559 int lockedp;
560 SCM owner;
561 /* how much the owner owns us. */
562 int level;
563 /* the threads waiting for this mutex. */
564 SCM waiting;
565 } fair_mutex;
566
567 static SCM
568 fair_mutex_mark (SCM mx)
569 {
570 fair_mutex *m = SCM_MUTEX_DATA (mx);
571 scm_gc_mark (m->owner);
572 return m->waiting;
573 }
574
575 SCM_DEFINE (scm_make_fair_mutex, "make-fair-mutex", 0, 0, 0,
576 (void),
577 "Create a new fair mutex object. ")
578 #define FUNC_NAME s_scm_make_fair_mutex
579 {
580 SCM mx = scm_make_smob (scm_tc16_fair_mutex);
581 fair_mutex *m = SCM_MUTEX_DATA (mx);
582 scm_i_plugin_mutex_init (&m->lock, &scm_i_plugin_mutex);
583 m->lockedp = 0;
584 m->owner = SCM_BOOL_F;
585 m->level = 0;
586 m->waiting = make_queue ();
587 return mx;
588 }
589 #undef FUNC_NAME
590
591 static int
592 fair_mutex_lock (fair_mutex *m)
593 {
594 scm_i_plugin_mutex_lock (&m->lock);
595 #if 0
596 /* Need to wait if another thread is just temporarily unlocking.
597 This is happens very seldom and only when the other thread is
598 between scm_mutex_unlock and scm_i_plugin_mutex_lock below. */
599 while (m->lockedp)
600 SCM_TICK;
601 m->lockedp = 1;
602 #endif
603
604 if (m->owner == SCM_BOOL_F)
605 m->owner = cur_thread;
606 else if (m->owner == cur_thread)
607 m->level++;
608 else
609 {
610 while (1)
611 {
612 SCM c = enqueue (m->waiting, cur_thread);
613 int err;
614 /* Note: It's important that m->lock is never locked for
615 any longer amount of time since that could prevent GC */
616 scm_i_plugin_mutex_unlock (&m->lock);
617 err = block ();
618 if (m->owner == cur_thread)
619 return 0;
620 scm_i_plugin_mutex_lock (&m->lock);
621 remqueue (m->waiting, c);
622 scm_i_plugin_mutex_unlock (&m->lock);
623 if (err)
624 return err;
625 SCM_ASYNC_TICK;
626 scm_i_plugin_mutex_lock (&m->lock);
627 }
628 }
629 scm_i_plugin_mutex_unlock (&m->lock);
630 return 0;
631 }
632
633 static int
634 fair_mutex_trylock (fair_mutex *m)
635 {
636 scm_i_plugin_mutex_lock (&m->lock);
637 if (m->owner == SCM_BOOL_F)
638 m->owner = cur_thread;
639 else if (m->owner == cur_thread)
640 m->level++;
641 else
642 {
643 scm_i_plugin_mutex_unlock (&m->lock);
644 return EBUSY;
645 }
646 scm_i_plugin_mutex_unlock (&m->lock);
647 return 0;
648 }
649
650 static int
651 fair_mutex_unlock (fair_mutex *m)
652 {
653 scm_i_plugin_mutex_lock (&m->lock);
654 if (m->owner != cur_thread)
655 {
656 scm_i_plugin_mutex_unlock (&m->lock);
657 return EPERM;
658 }
659 else if (m->level > 0)
660 m->level--;
661 else
662 {
663 SCM next = dequeue (m->waiting);
664 if (!SCM_FALSEP (next))
665 {
666 m->owner = next;
667 unblock (SCM_THREAD_DATA (next));
668 }
669 else
670 m->owner = SCM_BOOL_F;
671 }
672 scm_i_plugin_mutex_unlock (&m->lock);
673 return 0;
674 }
675
676 /*** Fair condition variables */
677
678 /* Like mutexes, we implement our own condition variables using the
679 primitives above.
680 */
681
682 typedef struct fair_cond {
683 scm_t_mutex lock;
684 /* the threads waiting for this condition. */
685 SCM waiting;
686 } fair_cond;
687
688 static SCM
689 fair_cond_mark (SCM cv)
690 {
691 fair_cond *c = SCM_CONDVAR_DATA (cv);
692 return c->waiting;
693 }
694
695 SCM_DEFINE (scm_make_fair_condition_variable, "make-fair-condition-variable", 0, 0, 0,
696 (void),
697 "Make a new fair condition variable.")
698 #define FUNC_NAME s_scm_make_fair_condition_variable
699 {
700 SCM cv = scm_make_smob (scm_tc16_fair_condvar);
701 fair_cond *c = SCM_CONDVAR_DATA (cv);
702 scm_i_plugin_mutex_init (&c->lock, 0);
703 c->waiting = make_queue ();
704 return cv;
705 }
706 #undef FUNC_NAME
707
708 static int
709 fair_cond_timedwait (fair_cond *c,
710 fair_mutex *m,
711 const struct timespec *waittime)
712 {
713 int err;
714 scm_i_plugin_mutex_lock (&c->lock);
715
716 while (1)
717 {
718 enqueue (c->waiting, cur_thread);
719 scm_i_plugin_mutex_unlock (&c->lock);
720 fair_mutex_unlock (m); /*fixme* - not thread safe */
721 if (waittime == NULL)
722 err = block ();
723 else
724 err = timed_block (waittime);
725 fair_mutex_lock (m);
726 if (err)
727 return err;
728 /* XXX - check whether we have been signalled. */
729 break;
730 }
731 return err;
732 }
733
734 static int
735 fair_cond_signal (fair_cond *c)
736 {
737 SCM th;
738 scm_i_plugin_mutex_lock (&c->lock);
739 if (!SCM_FALSEP (th = dequeue (c->waiting)))
740 unblock (SCM_THREAD_DATA (th));
741 scm_i_plugin_mutex_unlock (&c->lock);
742 return 0;
743 }
744
745 static int
746 fair_cond_broadcast (fair_cond *c)
747 {
748 SCM th;
749 scm_i_plugin_mutex_lock (&c->lock);
750 while (!SCM_FALSEP (th = dequeue (c->waiting)))
751 unblock (SCM_THREAD_DATA (th));
752 scm_i_plugin_mutex_unlock (&c->lock);
753 return 0;
754 }
755
756 /*** Mutexes */
757
758 SCM_DEFINE (scm_make_mutex, "make-mutex", 0, 0, 0,
759 (void),
760 "Create a new mutex object. ")
761 #define FUNC_NAME s_scm_make_mutex
762 {
763 SCM mx = scm_make_smob (scm_tc16_mutex);
764 scm_i_plugin_mutex_init (SCM_MUTEX_DATA (mx), &scm_i_plugin_mutex);
765 return mx;
766 }
767 #undef FUNC_NAME
768
769 /*fixme* change documentation */
770 SCM_DEFINE (scm_lock_mutex, "lock-mutex", 1, 0, 0,
771 (SCM mx),
772 "Lock @var{mutex}. If the mutex is already locked, the calling thread "
773 "blocks until the mutex becomes available. The function returns when "
774 "the calling thread owns the lock on @var{mutex}. Locking a mutex that "
775 "a thread already owns will succeed right away and will not block the "
776 "thread. That is, Guile's mutexes are @emph{recursive}. ")
777 #define FUNC_NAME s_scm_lock_mutex
778 {
779 int err;
780 SCM_VALIDATE_MUTEX (1, mx);
781
782 if (SCM_TYP16 (mx) == scm_tc16_fair_mutex)
783 err = fair_mutex_lock (SCM_MUTEX_DATA (mx));
784 else
785 {
786 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
787 scm_thread *t = scm_i_leave_guile ();
788 err = scm_i_plugin_mutex_lock (m);
789 scm_i_enter_guile (t);
790 }
791
792 if (err)
793 {
794 errno = err;
795 SCM_SYSERROR;
796 }
797 return SCM_BOOL_T;
798 }
799 #undef FUNC_NAME
800
801 SCM_DEFINE (scm_try_mutex, "try-mutex", 1, 0, 0,
802 (SCM mx),
803 "Try to lock @var{mutex}. If the mutex is already locked by someone "
804 "else, return @code{#f}. Else lock the mutex and return @code{#t}. ")
805 #define FUNC_NAME s_scm_try_mutex
806 {
807 int err;
808 SCM_VALIDATE_MUTEX (1, mx);
809
810 if (SCM_TYP16 (mx) == scm_tc16_fair_mutex)
811 err = fair_mutex_trylock (SCM_MUTEX_DATA (mx));
812 else
813 {
814 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
815 scm_thread *t = scm_i_leave_guile ();
816 err = scm_i_plugin_mutex_trylock (m);
817 scm_i_enter_guile (t);
818 }
819
820 if (err == EBUSY)
821 return SCM_BOOL_F;
822
823 if (err)
824 {
825 errno = err;
826 SCM_SYSERROR;
827 }
828
829 return SCM_BOOL_T;
830 }
831 #undef FUNC_NAME
832
833 SCM_DEFINE (scm_unlock_mutex, "unlock-mutex", 1, 0, 0,
834 (SCM mx),
835 "Unlocks @var{mutex} if the calling thread owns the lock on "
836 "@var{mutex}. Calling unlock-mutex on a mutex not owned by the current "
837 "thread results in undefined behaviour. Once a mutex has been unlocked, "
838 "one thread blocked on @var{mutex} is awakened and grabs the mutex "
839 "lock. Every call to @code{lock-mutex} by this thread must be matched "
840 "with a call to @code{unlock-mutex}. Only the last call to "
841 "@code{unlock-mutex} will actually unlock the mutex. ")
842 #define FUNC_NAME s_scm_unlock_mutex
843 {
844 int err;
845 SCM_VALIDATE_MUTEX (1, mx);
846
847 if (SCM_TYP16 (mx) == scm_tc16_fair_mutex)
848 {
849 err = fair_mutex_unlock (SCM_MUTEX_DATA (mx));
850 if (err == EPERM)
851 {
852 fair_mutex *m = SCM_MUTEX_DATA (mx);
853 if (m->owner != cur_thread)
854 {
855 if (m->owner == SCM_BOOL_F)
856 SCM_MISC_ERROR ("mutex not locked", SCM_EOL);
857 else
858 SCM_MISC_ERROR ("mutex not locked by this thread", SCM_EOL);
859 }
860 }
861 }
862 else
863 {
864 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
865 err = scm_i_plugin_mutex_unlock (m);
866 }
867
868 if (err)
869 {
870 errno = err;
871 SCM_SYSERROR;
872 }
873 return SCM_BOOL_T;
874 }
875 #undef FUNC_NAME
876
877 /*** Condition variables */
878
879 SCM_DEFINE (scm_make_condition_variable, "make-condition-variable", 0, 0, 0,
880 (void),
881 "Make a new condition variable.")
882 #define FUNC_NAME s_scm_make_condition_variable
883 {
884 SCM cv = scm_make_smob (scm_tc16_condvar);
885 scm_i_plugin_cond_init (SCM_CONDVAR_DATA (cv), 0);
886 return cv;
887 }
888 #undef FUNC_NAME
889
890 SCM_DEFINE (scm_timed_wait_condition_variable, "wait-condition-variable", 2, 1, 0,
891 (SCM cv, SCM mx, SCM t),
892 "Wait until @var{cond-var} has been signalled. While waiting, "
893 "@var{mutex} is atomically unlocked (as with @code{unlock-mutex}) and "
894 "is locked again when this function returns. When @var{time} is given, "
895 "it specifies a point in time where the waiting should be aborted. It "
896 "can be either a integer as returned by @code{current-time} or a pair "
897 "as returned by @code{gettimeofday}. When the waiting is aborted the "
898 "mutex is locked and @code{#f} is returned. When the condition "
899 "variable is in fact signalled, the mutex is also locked and @code{#t} "
900 "is returned. ")
901 #define FUNC_NAME s_scm_timed_wait_condition_variable
902 {
903 struct timespec waittime;
904 int err;
905
906 SCM_VALIDATE_CONDVAR (1, cv);
907 SCM_VALIDATE_MUTEX (2, mx);
908 if (!((SCM_TYP16 (cv) == scm_tc16_condvar
909 && SCM_TYP16 (mx) == scm_tc16_mutex)
910 || (SCM_TYP16 (cv) == scm_tc16_fair_condvar
911 && SCM_TYP16 (mx) == scm_tc16_fair_mutex)))
912 SCM_MISC_ERROR ("Condition variable and mutex are of different kinds.",
913 SCM_EOL);
914
915 if (!SCM_UNBNDP (t))
916 {
917 if (SCM_CONSP (t))
918 {
919 SCM_VALIDATE_UINT_COPY (3, SCM_CAR (t), waittime.tv_sec);
920 SCM_VALIDATE_UINT_COPY (3, SCM_CDR (t), waittime.tv_nsec);
921 waittime.tv_nsec *= 1000;
922 }
923 else
924 {
925 SCM_VALIDATE_UINT_COPY (3, t, waittime.tv_sec);
926 waittime.tv_nsec = 0;
927 }
928 }
929
930 if (SCM_TYP16 (cv) == scm_tc16_fair_condvar)
931 err = fair_cond_timedwait (SCM_CONDVAR_DATA (cv),
932 SCM_MUTEX_DATA (mx),
933 SCM_UNBNDP (t) ? NULL : &waittime);
934 else
935 {
936 scm_t_cond *c = SCM_CONDVAR_DATA (cv);
937 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
938 scm_thread *t = scm_i_leave_guile ();
939 err = scm_i_plugin_cond_wait (c, m);
940 scm_i_enter_guile (t);
941 }
942
943 if (err)
944 {
945 errno = err;
946 SCM_SYSERROR;
947 }
948 return SCM_BOOL_T;
949 }
950 #undef FUNC_NAME
951
952 SCM_DEFINE (scm_signal_condition_variable, "signal-condition-variable", 1, 0, 0,
953 (SCM cv),
954 "Wake up one thread that is waiting for @var{cv}")
955 #define FUNC_NAME s_scm_signal_condition_variable
956 {
957 SCM_VALIDATE_CONDVAR (1, cv);
958 if (SCM_TYP16 (cv) == scm_tc16_fair_condvar)
959 fair_cond_signal (SCM_CONDVAR_DATA (cv));
960 else
961 {
962 scm_t_cond *c = SCM_CONDVAR_DATA (cv);
963 scm_i_plugin_cond_signal (c);
964 }
965 return SCM_BOOL_T;
966 }
967 #undef FUNC_NAME
968
969 SCM_DEFINE (scm_broadcast_condition_variable, "broadcast-condition-variable", 1, 0, 0,
970 (SCM cv),
971 "Wake up all threads that are waiting for @var{cv}. ")
972 #define FUNC_NAME s_scm_broadcast_condition_variable
973 {
974 SCM_VALIDATE_CONDVAR (1, cv);
975 if (SCM_TYP16 (cv) == scm_tc16_fair_condvar)
976 fair_cond_broadcast (SCM_CONDVAR_DATA (cv));
977 else
978 {
979 scm_t_cond *c = SCM_CONDVAR_DATA (cv);
980 scm_i_plugin_cond_broadcast (c);
981 }
982 return SCM_BOOL_T;
983 }
984 #undef FUNC_NAME
985
986 /*** Marking stacks */
987
988 /* XXX - what to do with this? Do we need to handle this for blocked
989 threads as well?
990 */
991 #ifdef __ia64__
992 # define SCM_MARK_BACKING_STORE() do { \
993 ucontext_t ctx; \
994 SCM_STACKITEM * top, * bot; \
995 getcontext (&ctx); \
996 scm_mark_locations ((SCM_STACKITEM *) &ctx.uc_mcontext, \
997 ((size_t) (sizeof (SCM_STACKITEM) - 1 + sizeof ctx.uc_mcontext) \
998 / sizeof (SCM_STACKITEM))); \
999 bot = (SCM_STACKITEM *) __libc_ia64_register_backing_store_base; \
1000 top = (SCM_STACKITEM *) ctx.uc_mcontext.sc_ar_bsp; \
1001 scm_mark_locations (bot, top - bot); } while (0)
1002 #else
1003 # define SCM_MARK_BACKING_STORE()
1004 #endif
1005
1006 void
1007 scm_threads_mark_stacks (void)
1008 {
1009 volatile SCM c;
1010 for (c = all_threads; !SCM_NULLP (c); c = SCM_CDR (c))
1011 {
1012 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (c));
1013 if (!THREAD_INITIALIZED_P (t))
1014 {
1015 /* Not fully initialized yet. */
1016 continue;
1017 }
1018 if (t->top == NULL)
1019 {
1020 long stack_len;
1021 #ifdef SCM_DEBUG
1022 if (t->thread != scm_thread_self ())
1023 abort ();
1024 #endif
1025 /* Active thread */
1026 /* stack_len is long rather than sizet in order to guarantee
1027 that &stack_len is long aligned */
1028 #ifdef STACK_GROWS_UP
1029 stack_len = ((SCM_STACKITEM *) (&t) -
1030 (SCM_STACKITEM *) thread->base);
1031
1032 /* Protect from the C stack. This must be the first marking
1033 * done because it provides information about what objects
1034 * are "in-use" by the C code. "in-use" objects are those
1035 * for which the information about length and base address must
1036 * remain usable. This requirement is stricter than a liveness
1037 * requirement -- in particular, it constrains the implementation
1038 * of scm_resizuve.
1039 */
1040 SCM_FLUSH_REGISTER_WINDOWS;
1041 /* This assumes that all registers are saved into the jmp_buf */
1042 setjmp (scm_save_regs_gc_mark);
1043 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
1044 ((size_t) sizeof scm_save_regs_gc_mark
1045 / sizeof (SCM_STACKITEM)));
1046
1047 scm_mark_locations (((size_t) t->base,
1048 (sizet) stack_len));
1049 #else
1050 stack_len = ((SCM_STACKITEM *) t->base -
1051 (SCM_STACKITEM *) (&t));
1052
1053 /* Protect from the C stack. This must be the first marking
1054 * done because it provides information about what objects
1055 * are "in-use" by the C code. "in-use" objects are those
1056 * for which the information about length and base address must
1057 * remain usable. This requirement is stricter than a liveness
1058 * requirement -- in particular, it constrains the implementation
1059 * of scm_resizuve.
1060 */
1061 SCM_FLUSH_REGISTER_WINDOWS;
1062 /* This assumes that all registers are saved into the jmp_buf */
1063 setjmp (scm_save_regs_gc_mark);
1064 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
1065 ((size_t) sizeof scm_save_regs_gc_mark
1066 / sizeof (SCM_STACKITEM)));
1067
1068 scm_mark_locations ((SCM_STACKITEM *) &t,
1069 stack_len);
1070 #endif
1071 }
1072 else
1073 {
1074 /* Suspended thread */
1075 #ifdef STACK_GROWS_UP
1076 long stack_len = t->top - t->base;
1077 scm_mark_locations (t->base, stack_len);
1078 #else
1079 long stack_len = t->base - t->top;
1080 scm_mark_locations (t->top, stack_len);
1081 #endif
1082 scm_mark_locations ((SCM_STACKITEM *) t->regs,
1083 ((size_t) sizeof(t->regs)
1084 / sizeof (SCM_STACKITEM)));
1085 }
1086 }
1087 }
1088
1089 /*** Select */
1090
1091 int
1092 scm_internal_select (int nfds,
1093 SELECT_TYPE *readfds,
1094 SELECT_TYPE *writefds,
1095 SELECT_TYPE *exceptfds,
1096 struct timeval *timeout)
1097 {
1098 int res, eno;
1099 scm_thread *c = scm_i_leave_guile ();
1100 res = scm_i_plugin_select (nfds, readfds, writefds, exceptfds, timeout);
1101 eno = errno;
1102 scm_i_enter_guile (c);
1103 SCM_ASYNC_TICK;
1104 errno = eno;
1105 return res;
1106 }
1107
1108 /* Low-level C API */
1109
1110 SCM
1111 scm_spawn_thread (scm_t_catch_body body, void *body_data,
1112 scm_t_catch_handler handler, void *handler_data)
1113 {
1114 return create_thread (body, body_data, handler, handler_data, SCM_BOOL_F);
1115 }
1116
1117 int
1118 scm_mutex_lock (scm_t_mutex *m)
1119 {
1120 scm_thread *t = scm_i_leave_guile ();
1121 int res = scm_i_plugin_mutex_lock (m);
1122 scm_i_enter_guile (t);
1123 return res;
1124 }
1125
1126 scm_t_rec_mutex *
1127 scm_make_rec_mutex ()
1128 {
1129 scm_t_rec_mutex *m = scm_malloc (sizeof (scm_t_rec_mutex));
1130 scm_i_plugin_rec_mutex_init (m, &scm_i_plugin_rec_mutex);
1131 return m;
1132 }
1133
1134 void
1135 scm_rec_mutex_free (scm_t_rec_mutex *m)
1136 {
1137 scm_i_plugin_rec_mutex_destroy (m);
1138 free (m);
1139 }
1140
1141 int
1142 scm_rec_mutex_lock (scm_t_rec_mutex *m)
1143 {
1144 scm_thread *t = scm_i_leave_guile ();
1145 int res = scm_i_plugin_rec_mutex_lock (m);
1146 scm_i_enter_guile (t);
1147 return res;
1148 }
1149
1150 int
1151 scm_cond_wait (scm_t_cond *c, scm_t_mutex *m)
1152 {
1153 scm_thread *t = scm_i_leave_guile ();
1154 scm_i_plugin_cond_wait (c, m);
1155 scm_i_enter_guile (t);
1156 return 0;
1157 }
1158
1159 int
1160 scm_cond_timedwait (scm_t_cond *c, scm_t_mutex *m, const struct timespec *wt)
1161 {
1162 scm_thread *t = scm_i_leave_guile ();
1163 int res = scm_i_plugin_cond_timedwait (c, m, wt);
1164 scm_i_enter_guile (t);
1165 return res;
1166 }
1167
1168 void
1169 scm_enter_guile ()
1170 {
1171 scm_i_enter_guile (SCM_CURRENT_THREAD);
1172 }
1173
1174 void
1175 scm_leave_guile ()
1176 {
1177 scm_i_leave_guile ();
1178 }
1179
1180 unsigned long
1181 scm_thread_usleep (unsigned long usecs)
1182 {
1183 struct timeval tv;
1184 tv.tv_usec = usecs % 1000000;
1185 tv.tv_sec = usecs / 1000000;
1186 scm_internal_select (0, NULL, NULL, NULL, &tv);
1187 return tv.tv_usec + tv.tv_sec*1000000;
1188 }
1189
1190 unsigned long
1191 scm_thread_sleep (unsigned long secs)
1192 {
1193 struct timeval tv;
1194 tv.tv_usec = 0;
1195 tv.tv_sec = secs;
1196 scm_internal_select (0, NULL, NULL, NULL, &tv);
1197 return tv.tv_sec;
1198 }
1199
1200 /*** Misc */
1201
1202 SCM_DEFINE (scm_current_thread, "current-thread", 0, 0, 0,
1203 (void),
1204 "Return the thread that called this function.")
1205 #define FUNC_NAME s_scm_current_thread
1206 {
1207 return cur_thread;
1208 }
1209 #undef FUNC_NAME
1210
1211 SCM_DEFINE (scm_all_threads, "all-threads", 0, 0, 0,
1212 (void),
1213 "Return a list of all threads.")
1214 #define FUNC_NAME s_scm_all_threads
1215 {
1216 return all_threads;
1217 }
1218 #undef FUNC_NAME
1219
1220 scm_root_state *
1221 scm_i_thread_root (SCM thread)
1222 {
1223 return ((scm_thread *) SCM_THREAD_DATA (thread))->root;
1224 }
1225
1226 SCM_DEFINE (scm_thread_exited_p, "thread-exited?", 1, 0, 0,
1227 (SCM thread),
1228 "Return @code{#t} iff @var{thread} has exited.\n")
1229 #define FUNC_NAME s_scm_thread_exited_p
1230 {
1231 return SCM_BOOL (scm_c_thread_exited_p (thread));
1232 }
1233 #undef FUNC_NAME
1234
1235 int
1236 scm_c_thread_exited_p (SCM thread)
1237 #define FUNC_NAME s_scm_thread_exited_p
1238 {
1239 scm_thread *t;
1240 SCM_VALIDATE_THREAD (1, thread);
1241 t = SCM_THREAD_DATA (thread);
1242 return t->exited;
1243 }
1244 #undef FUNC_NAME
1245
1246 static scm_t_cond wake_up_cond;
1247 int scm_i_thread_go_to_sleep;
1248 static scm_t_rec_mutex gc_section_mutex;
1249 static int gc_section_count = 0;
1250 static int threads_initialized_p = 0;
1251
1252 void
1253 scm_i_thread_put_to_sleep ()
1254 {
1255 scm_rec_mutex_lock (&gc_section_mutex);
1256 if (threads_initialized_p && !gc_section_count++)
1257 {
1258 SCM threads;
1259 scm_i_plugin_mutex_lock (&thread_admin_mutex);
1260 threads = all_threads;
1261 /* Signal all threads to go to sleep */
1262 scm_i_thread_go_to_sleep = 1;
1263 for (; !SCM_NULLP (threads); threads = SCM_CDR (threads))
1264 if (SCM_CAR (threads) != cur_thread)
1265 {
1266 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (threads));
1267 scm_i_plugin_mutex_lock (&t->heap_mutex);
1268 }
1269 scm_i_thread_go_to_sleep = 0;
1270 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
1271 }
1272 }
1273
1274 void
1275 scm_i_thread_invalidate_freelists ()
1276 {
1277 /* Don't need to lock thread_admin_mutex here since we are sinle threaded */
1278 SCM threads = all_threads;
1279 for (; !SCM_NULLP (threads); threads = SCM_CDR (threads))
1280 if (SCM_CAR (threads) != cur_thread)
1281 {
1282 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (threads));
1283 t->clear_freelists_p = 1;
1284 }
1285 }
1286
1287 void
1288 scm_i_thread_wake_up ()
1289 {
1290 if (threads_initialized_p && !--gc_section_count)
1291 {
1292 SCM threads;
1293 /* Need to lock since woken threads can die and be deleted from list */
1294 scm_i_plugin_mutex_lock (&thread_admin_mutex);
1295 threads = all_threads;
1296 scm_i_plugin_cond_broadcast (&wake_up_cond);
1297 for (; !SCM_NULLP (threads); threads = SCM_CDR (threads))
1298 if (SCM_CAR (threads) != cur_thread)
1299 {
1300 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (threads));
1301 scm_i_plugin_mutex_unlock (&t->heap_mutex);
1302 }
1303 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
1304 }
1305 scm_rec_mutex_unlock (&gc_section_mutex);
1306 }
1307
1308 void
1309 scm_i_thread_sleep_for_gc ()
1310 {
1311 scm_thread *t;
1312 t = suspend ();
1313 scm_i_plugin_cond_wait (&wake_up_cond, &t->heap_mutex);
1314 resume (t);
1315 }
1316
1317 scm_t_mutex scm_i_critical_section_mutex;
1318 scm_t_rec_mutex scm_i_defer_mutex;
1319
1320 #ifdef USE_PTHREAD_THREADS
1321 #include "libguile/pthread-threads.c"
1322 #endif
1323
1324 /*** Initialization */
1325
1326 void
1327 scm_threads_prehistory ()
1328 {
1329 scm_thread *t;
1330 #ifdef USE_PTHREAD_THREADS
1331 /* Must be called before any initialization of a mutex. */
1332 scm_init_pthread_threads ();
1333 #endif
1334 scm_i_plugin_mutex_init (&thread_admin_mutex, &scm_i_plugin_mutex);
1335 scm_i_plugin_rec_mutex_init (&gc_section_mutex, &scm_i_plugin_rec_mutex);
1336 scm_i_plugin_cond_init (&wake_up_cond, 0);
1337 scm_i_plugin_mutex_init (&scm_i_critical_section_mutex, &scm_i_plugin_mutex);
1338 thread_count = 1;
1339 scm_i_plugin_key_create (&scm_i_thread_key, 0);
1340 scm_i_plugin_key_create (&scm_i_root_state_key, 0);
1341 scm_i_plugin_rec_mutex_init (&scm_i_defer_mutex, &scm_i_plugin_rec_mutex);
1342 /* Allocate a fake thread object to be used during bootup. */
1343 t = malloc (sizeof (scm_thread));
1344 t->base = NULL;
1345 t->clear_freelists_p = 0;
1346 scm_setspecific (scm_i_thread_key, t);
1347 }
1348
1349 scm_t_bits scm_tc16_thread;
1350 scm_t_bits scm_tc16_future;
1351 scm_t_bits scm_tc16_mutex;
1352 scm_t_bits scm_tc16_fair_mutex;
1353 scm_t_bits scm_tc16_condvar;
1354 scm_t_bits scm_tc16_fair_condvar;
1355
1356 void
1357 scm_init_threads (SCM_STACKITEM *base)
1358 {
1359 SCM thread;
1360 scm_tc16_thread = scm_make_smob_type ("thread", sizeof (scm_thread));
1361 scm_tc16_mutex = scm_make_smob_type ("mutex", sizeof (scm_t_mutex));
1362 scm_tc16_fair_mutex = scm_make_smob_type ("fair-mutex",
1363 sizeof (fair_mutex));
1364 scm_tc16_condvar = scm_make_smob_type ("condition-variable",
1365 sizeof (scm_t_cond));
1366 scm_tc16_fair_condvar = scm_make_smob_type ("fair-condition-variable",
1367 sizeof (fair_cond));
1368
1369 thread = make_thread (SCM_BOOL_F);
1370 /* Replace initial fake thread with a real thread object */
1371 free (SCM_CURRENT_THREAD);
1372 scm_setspecific (scm_i_thread_key, SCM_THREAD_DATA (thread));
1373 scm_i_enter_guile (SCM_CURRENT_THREAD);
1374
1375 /* root is set later from init.c */
1376 init_thread_creatant (thread, base);
1377 thread_count = 1;
1378 scm_gc_register_root (&all_threads);
1379 all_threads = scm_cons (thread, SCM_EOL);
1380
1381 scm_set_smob_mark (scm_tc16_thread, thread_mark);
1382 scm_set_smob_print (scm_tc16_thread, thread_print);
1383 scm_set_smob_free (scm_tc16_thread, thread_free);
1384
1385 scm_set_smob_mark (scm_tc16_fair_mutex, fair_mutex_mark);
1386
1387 scm_set_smob_mark (scm_tc16_fair_condvar, fair_cond_mark);
1388
1389 scm_tc16_future = scm_make_smob_type ("future", 0);
1390 scm_set_smob_mark (scm_tc16_future, scm_markcdr);
1391 scm_set_smob_free (scm_tc16_future, future_free);
1392 scm_set_smob_print (scm_tc16_future, future_print);
1393
1394 threads_initialized_p = 1;
1395 }
1396
1397 void
1398 scm_init_thread_procs ()
1399 {
1400 scm_loc_sys_thread_handler
1401 = SCM_VARIABLE_LOC (scm_c_define ("%thread-handler", SCM_BOOL_F));
1402 #include "libguile/threads.x"
1403 }
1404
1405 /* XXX */
1406
1407 void
1408 scm_init_iselect ()
1409 {
1410 }
1411
1412 /*
1413 Local Variables:
1414 c-file-style: "gnu"
1415 End:
1416 */