* threads.c (scm_timed_wait_condition_variable): Support timed
[bpt/guile.git] / libguile / threads.c
1 /* Copyright (C) 1995,1996,1997,1998,2000,2001, 2002, 2003 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 /* detach before unlocking in order to not become joined when detached */
350 scm_thread_detach (t->thread);
351 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
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 /* disconnect from parent, to prevent remembering dead threads */
401 t->root->parent = SCM_BOOL_F;
402
403 /* In order to avoid the need of synchronization between parent
404 and child thread, we need to insert the child into all_threads
405 before creation. */
406 {
407 SCM new_threads = scm_cons (thread, SCM_BOOL_F); /* could cause GC */
408 scm_thread *parent = scm_i_leave_guile (); /* to prevent deadlock */
409 scm_i_plugin_mutex_lock (&thread_admin_mutex);
410 SCM_SETCDR (new_threads, all_threads);
411 all_threads = new_threads;
412 thread_count++;
413 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
414
415 scm_remember_upto_here_1 (root);
416
417 scm_i_enter_guile (parent);
418 }
419
420 err = scm_i_plugin_thread_create (&th, 0, launch_thread, (void *) data);
421 if (err != 0)
422 {
423 scm_i_plugin_mutex_lock (&thread_admin_mutex);
424 all_threads = scm_delq_x (thread, all_threads);
425 ((scm_thread *) SCM_THREAD_DATA(thread))->exited = 1;
426 thread_count--;
427 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
428 }
429
430 /* Return to old dynamic context. */
431 scm_dowinds (old_winds, - scm_ilength (old_winds));
432
433 if (err)
434 {
435 errno = err;
436 scm_syserror ("create-thread");
437 }
438 }
439
440 return thread;
441 }
442
443 SCM_DEFINE (scm_call_with_new_thread, "call-with-new-thread", 2, 0, 0,
444 (SCM thunk, SCM handler),
445 "Evaluate @var{(thunk)} in a new thread, and new dynamic context, "
446 "returning a new thread object representing the thread. "
447 "If an error occurs during evaluation, call error-thunk, passing it an "
448 "error code describing the condition. "
449 "If this happens, the error-thunk is called outside the scope of the new "
450 "root -- it is called in the same dynamic context in which "
451 "with-new-thread was evaluated, but not in the callers thread. "
452 "All the evaluation rules for dynamic roots apply to threads.")
453 #define FUNC_NAME s_scm_call_with_new_thread
454 {
455 SCM_ASSERT (SCM_NFALSEP (scm_thunk_p (thunk)), thunk, SCM_ARG1, FUNC_NAME);
456 SCM_ASSERT (SCM_NFALSEP (scm_procedure_p (handler)), handler, SCM_ARG2,
457 FUNC_NAME);
458
459 return create_thread ((scm_t_catch_body) scm_call_0, thunk,
460 (scm_t_catch_handler) scm_apply_1, handler,
461 scm_cons (thunk, handler));
462 }
463 #undef FUNC_NAME
464
465 SCM_DEFINE (scm_yield, "yield", 0, 0, 0,
466 (),
467 "Move the calling thread to the end of the scheduling queue.")
468 #define FUNC_NAME s_scm_yield
469 {
470 return SCM_BOOL (scm_thread_yield);
471 }
472 #undef FUNC_NAME
473
474 SCM_DEFINE (scm_join_thread, "join-thread", 1, 0, 0,
475 (SCM thread),
476 "Suspend execution of the calling thread until the target @var{thread} "
477 "terminates, unless the target @var{thread} has already terminated. ")
478 #define FUNC_NAME s_scm_join_thread
479 {
480 scm_thread *t;
481 SCM res;
482
483 SCM_VALIDATE_THREAD (1, thread);
484 if (SCM_EQ_P (cur_thread, thread))
485 SCM_MISC_ERROR ("can not join the current thread", SCM_EOL);
486
487 t = SCM_THREAD_DATA (thread);
488 if (!t->exited)
489 {
490 scm_thread *c;
491 c = scm_i_leave_guile ();
492 while (!THREAD_INITIALIZED_P (t))
493 scm_i_plugin_thread_yield ();
494 scm_thread_join (t->thread, 0);
495 scm_i_enter_guile (c);
496 }
497 res = t->result;
498 t->result = SCM_BOOL_F;
499 return res;
500 }
501 #undef FUNC_NAME
502
503 SCM *scm_loc_sys_thread_handler;
504
505 SCM
506 scm_i_make_future (SCM thunk)
507 {
508 SCM_RETURN_NEWSMOB2 (scm_tc16_future,
509 create_thread ((scm_t_catch_body) scm_call_0,
510 thunk,
511 (scm_t_catch_handler) scm_apply_1,
512 *scm_loc_sys_thread_handler,
513 scm_cons (thunk,
514 *scm_loc_sys_thread_handler)),
515 scm_make_rec_mutex ());
516 }
517
518 static size_t
519 future_free (SCM future)
520 {
521 scm_rec_mutex_free (SCM_FUTURE_MUTEX (future));
522 return 0;
523 }
524
525 static int
526 future_print (SCM exp, SCM port, scm_print_state *pstate)
527 {
528 int writingp = SCM_WRITINGP (pstate);
529 scm_puts ("#<future ", port);
530 SCM_SET_WRITINGP (pstate, 1);
531 scm_iprin1 (SCM_FUTURE_DATA (exp), port, pstate);
532 SCM_SET_WRITINGP (pstate, writingp);
533 scm_putc ('>', port);
534 return !0;
535 }
536
537 SCM_DEFINE (scm_future_ref, "future-ref", 1, 0, 0,
538 (SCM future),
539 "If the future @var{x} has not been computed yet, compute and\n"
540 "return @var{x}, otherwise just return the previously computed\n"
541 "value.")
542 #define FUNC_NAME s_scm_future_ref
543 {
544 SCM_VALIDATE_FUTURE (1, future);
545 scm_rec_mutex_lock (SCM_FUTURE_MUTEX (future));
546 if (!SCM_FUTURE_COMPUTED_P (future))
547 {
548 SCM value = scm_join_thread (SCM_FUTURE_DATA (future));
549 if (!SCM_FUTURE_COMPUTED_P (future))
550 {
551 SCM_SET_FUTURE_DATA (future, value);
552 SCM_SET_FUTURE_COMPUTED (future);
553 }
554 }
555 scm_rec_mutex_unlock (SCM_FUTURE_MUTEX (future));
556 return SCM_FUTURE_DATA (future);
557 }
558 #undef FUNC_NAME
559
560 /*** Fair mutexes */
561
562 /* We implement our own mutex type since we want them to be 'fair', we
563 want to do fancy things while waiting for them (like running
564 asyncs) and we want to support waiting on many things at once.
565 Also, we might add things that are nice for debugging.
566 */
567
568 typedef struct fair_mutex {
569 /* the thread currently owning the mutex, or SCM_BOOL_F. */
570 scm_t_mutex lock;
571 int lockedp;
572 SCM owner;
573 /* how much the owner owns us. */
574 int level;
575 /* the threads waiting for this mutex. */
576 SCM waiting;
577 } fair_mutex;
578
579 static SCM
580 fair_mutex_mark (SCM mx)
581 {
582 fair_mutex *m = SCM_MUTEX_DATA (mx);
583 scm_gc_mark (m->owner);
584 return m->waiting;
585 }
586
587 SCM_DEFINE (scm_make_fair_mutex, "make-fair-mutex", 0, 0, 0,
588 (void),
589 "Create a new fair mutex object. ")
590 #define FUNC_NAME s_scm_make_fair_mutex
591 {
592 SCM mx = scm_make_smob (scm_tc16_fair_mutex);
593 fair_mutex *m = SCM_MUTEX_DATA (mx);
594 scm_i_plugin_mutex_init (&m->lock, &scm_i_plugin_mutex);
595 m->lockedp = 0;
596 m->owner = SCM_BOOL_F;
597 m->level = 0;
598 m->waiting = make_queue ();
599 return mx;
600 }
601 #undef FUNC_NAME
602
603 static int
604 fair_mutex_lock (fair_mutex *m)
605 {
606 scm_i_plugin_mutex_lock (&m->lock);
607 #if 0
608 /* Need to wait if another thread is just temporarily unlocking.
609 This is happens very seldom and only when the other thread is
610 between scm_mutex_unlock and scm_i_plugin_mutex_lock below. */
611 while (m->lockedp)
612 SCM_TICK;
613 m->lockedp = 1;
614 #endif
615
616 if (m->owner == SCM_BOOL_F)
617 m->owner = cur_thread;
618 else if (m->owner == cur_thread)
619 m->level++;
620 else
621 {
622 while (1)
623 {
624 SCM c = enqueue (m->waiting, cur_thread);
625 int err;
626 /* Note: It's important that m->lock is never locked for
627 any longer amount of time since that could prevent GC */
628 scm_i_plugin_mutex_unlock (&m->lock);
629 err = block ();
630 if (m->owner == cur_thread)
631 return 0;
632 scm_i_plugin_mutex_lock (&m->lock);
633 remqueue (m->waiting, c);
634 scm_i_plugin_mutex_unlock (&m->lock);
635 if (err)
636 return err;
637 SCM_ASYNC_TICK;
638 scm_i_plugin_mutex_lock (&m->lock);
639 }
640 }
641 scm_i_plugin_mutex_unlock (&m->lock);
642 return 0;
643 }
644
645 static int
646 fair_mutex_trylock (fair_mutex *m)
647 {
648 scm_i_plugin_mutex_lock (&m->lock);
649 if (m->owner == SCM_BOOL_F)
650 m->owner = cur_thread;
651 else if (m->owner == cur_thread)
652 m->level++;
653 else
654 {
655 scm_i_plugin_mutex_unlock (&m->lock);
656 return EBUSY;
657 }
658 scm_i_plugin_mutex_unlock (&m->lock);
659 return 0;
660 }
661
662 static int
663 fair_mutex_unlock (fair_mutex *m)
664 {
665 scm_i_plugin_mutex_lock (&m->lock);
666 if (m->owner != cur_thread)
667 {
668 scm_i_plugin_mutex_unlock (&m->lock);
669 return EPERM;
670 }
671 else if (m->level > 0)
672 m->level--;
673 else
674 {
675 SCM next = dequeue (m->waiting);
676 if (!SCM_FALSEP (next))
677 {
678 m->owner = next;
679 unblock (SCM_THREAD_DATA (next));
680 }
681 else
682 m->owner = SCM_BOOL_F;
683 }
684 scm_i_plugin_mutex_unlock (&m->lock);
685 return 0;
686 }
687
688 /*** Fair condition variables */
689
690 /* Like mutexes, we implement our own condition variables using the
691 primitives above.
692 */
693
694 typedef struct fair_cond {
695 scm_t_mutex lock;
696 /* the threads waiting for this condition. */
697 SCM waiting;
698 } fair_cond;
699
700 static SCM
701 fair_cond_mark (SCM cv)
702 {
703 fair_cond *c = SCM_CONDVAR_DATA (cv);
704 return c->waiting;
705 }
706
707 SCM_DEFINE (scm_make_fair_condition_variable, "make-fair-condition-variable", 0, 0, 0,
708 (void),
709 "Make a new fair condition variable.")
710 #define FUNC_NAME s_scm_make_fair_condition_variable
711 {
712 SCM cv = scm_make_smob (scm_tc16_fair_condvar);
713 fair_cond *c = SCM_CONDVAR_DATA (cv);
714 scm_i_plugin_mutex_init (&c->lock, 0);
715 c->waiting = make_queue ();
716 return cv;
717 }
718 #undef FUNC_NAME
719
720 static int
721 fair_cond_timedwait (fair_cond *c,
722 fair_mutex *m,
723 const struct timespec *waittime)
724 {
725 int err;
726 scm_i_plugin_mutex_lock (&c->lock);
727
728 while (1)
729 {
730 enqueue (c->waiting, cur_thread);
731 scm_i_plugin_mutex_unlock (&c->lock);
732 fair_mutex_unlock (m); /*fixme* - not thread safe */
733 if (waittime == NULL)
734 err = block ();
735 else
736 err = timed_block (waittime);
737 fair_mutex_lock (m);
738 if (err)
739 return err;
740 /* XXX - check whether we have been signalled. */
741 break;
742 }
743 return err;
744 }
745
746 static int
747 fair_cond_signal (fair_cond *c)
748 {
749 SCM th;
750 scm_i_plugin_mutex_lock (&c->lock);
751 if (!SCM_FALSEP (th = dequeue (c->waiting)))
752 unblock (SCM_THREAD_DATA (th));
753 scm_i_plugin_mutex_unlock (&c->lock);
754 return 0;
755 }
756
757 static int
758 fair_cond_broadcast (fair_cond *c)
759 {
760 SCM th;
761 scm_i_plugin_mutex_lock (&c->lock);
762 while (!SCM_FALSEP (th = dequeue (c->waiting)))
763 unblock (SCM_THREAD_DATA (th));
764 scm_i_plugin_mutex_unlock (&c->lock);
765 return 0;
766 }
767
768 /*** Mutexes */
769
770 SCM_DEFINE (scm_make_mutex, "make-mutex", 0, 0, 0,
771 (void),
772 "Create a new mutex object. ")
773 #define FUNC_NAME s_scm_make_mutex
774 {
775 SCM mx = scm_make_smob (scm_tc16_mutex);
776 scm_i_plugin_mutex_init (SCM_MUTEX_DATA (mx), &scm_i_plugin_mutex);
777 return mx;
778 }
779 #undef FUNC_NAME
780
781 /*fixme* change documentation */
782 SCM_DEFINE (scm_lock_mutex, "lock-mutex", 1, 0, 0,
783 (SCM mx),
784 "Lock @var{mutex}. If the mutex is already locked, the calling thread "
785 "blocks until the mutex becomes available. The function returns when "
786 "the calling thread owns the lock on @var{mutex}. Locking a mutex that "
787 "a thread already owns will succeed right away and will not block the "
788 "thread. That is, Guile's mutexes are @emph{recursive}. ")
789 #define FUNC_NAME s_scm_lock_mutex
790 {
791 int err;
792 SCM_VALIDATE_MUTEX (1, mx);
793
794 if (SCM_TYP16 (mx) == scm_tc16_fair_mutex)
795 err = fair_mutex_lock (SCM_MUTEX_DATA (mx));
796 else
797 {
798 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
799 err = scm_mutex_lock (m);
800 }
801
802 if (err)
803 {
804 errno = err;
805 SCM_SYSERROR;
806 }
807 return SCM_BOOL_T;
808 }
809 #undef FUNC_NAME
810
811 SCM_DEFINE (scm_try_mutex, "try-mutex", 1, 0, 0,
812 (SCM mx),
813 "Try to lock @var{mutex}. If the mutex is already locked by someone "
814 "else, return @code{#f}. Else lock the mutex and return @code{#t}. ")
815 #define FUNC_NAME s_scm_try_mutex
816 {
817 int err;
818 SCM_VALIDATE_MUTEX (1, mx);
819
820 if (SCM_TYP16 (mx) == scm_tc16_fair_mutex)
821 err = fair_mutex_trylock (SCM_MUTEX_DATA (mx));
822 else
823 {
824 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
825 err = scm_mutex_trylock (m);
826 }
827
828 if (err == EBUSY)
829 return SCM_BOOL_F;
830
831 if (err)
832 {
833 errno = err;
834 SCM_SYSERROR;
835 }
836
837 return SCM_BOOL_T;
838 }
839 #undef FUNC_NAME
840
841 SCM_DEFINE (scm_unlock_mutex, "unlock-mutex", 1, 0, 0,
842 (SCM mx),
843 "Unlocks @var{mutex} if the calling thread owns the lock on "
844 "@var{mutex}. Calling unlock-mutex on a mutex not owned by the current "
845 "thread results in undefined behaviour. Once a mutex has been unlocked, "
846 "one thread blocked on @var{mutex} is awakened and grabs the mutex "
847 "lock. Every call to @code{lock-mutex} by this thread must be matched "
848 "with a call to @code{unlock-mutex}. Only the last call to "
849 "@code{unlock-mutex} will actually unlock the mutex. ")
850 #define FUNC_NAME s_scm_unlock_mutex
851 {
852 int err;
853 SCM_VALIDATE_MUTEX (1, mx);
854
855 if (SCM_TYP16 (mx) == scm_tc16_fair_mutex)
856 {
857 err = fair_mutex_unlock (SCM_MUTEX_DATA (mx));
858 if (err == EPERM)
859 {
860 fair_mutex *m = SCM_MUTEX_DATA (mx);
861 if (m->owner != cur_thread)
862 {
863 if (m->owner == SCM_BOOL_F)
864 SCM_MISC_ERROR ("mutex not locked", SCM_EOL);
865 else
866 SCM_MISC_ERROR ("mutex not locked by this thread", SCM_EOL);
867 }
868 }
869 }
870 else
871 {
872 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
873 err = scm_mutex_unlock (m);
874 }
875
876 if (err)
877 {
878 errno = err;
879 SCM_SYSERROR;
880 }
881 return SCM_BOOL_T;
882 }
883 #undef FUNC_NAME
884
885 /*** Condition variables */
886
887 SCM_DEFINE (scm_make_condition_variable, "make-condition-variable", 0, 0, 0,
888 (void),
889 "Make a new condition variable.")
890 #define FUNC_NAME s_scm_make_condition_variable
891 {
892 SCM cv = scm_make_smob (scm_tc16_condvar);
893 scm_i_plugin_cond_init (SCM_CONDVAR_DATA (cv), 0);
894 return cv;
895 }
896 #undef FUNC_NAME
897
898 SCM_DEFINE (scm_timed_wait_condition_variable, "wait-condition-variable", 2, 1, 0,
899 (SCM cv, SCM mx, SCM t),
900 "Wait until @var{cond-var} has been signalled. While waiting, "
901 "@var{mutex} is atomically unlocked (as with @code{unlock-mutex}) and "
902 "is locked again when this function returns. When @var{time} is given, "
903 "it specifies a point in time where the waiting should be aborted. It "
904 "can be either a integer as returned by @code{current-time} or a pair "
905 "as returned by @code{gettimeofday}. When the waiting is aborted the "
906 "mutex is locked and @code{#f} is returned. When the condition "
907 "variable is in fact signalled, the mutex is also locked and @code{#t} "
908 "is returned. ")
909 #define FUNC_NAME s_scm_timed_wait_condition_variable
910 {
911 struct timespec waittime;
912 int err;
913
914 SCM_VALIDATE_CONDVAR (1, cv);
915 SCM_VALIDATE_MUTEX (2, mx);
916 if (!((SCM_TYP16 (cv) == scm_tc16_condvar
917 && SCM_TYP16 (mx) == scm_tc16_mutex)
918 || (SCM_TYP16 (cv) == scm_tc16_fair_condvar
919 && SCM_TYP16 (mx) == scm_tc16_fair_mutex)))
920 SCM_MISC_ERROR ("Condition variable and mutex are of different kinds.",
921 SCM_EOL);
922
923 if (!SCM_UNBNDP (t))
924 {
925 if (SCM_CONSP (t))
926 {
927 SCM_VALIDATE_UINT_COPY (3, SCM_CAR (t), waittime.tv_sec);
928 SCM_VALIDATE_UINT_COPY (3, SCM_CDR (t), waittime.tv_nsec);
929 waittime.tv_nsec *= 1000;
930 }
931 else
932 {
933 SCM_VALIDATE_UINT_COPY (3, t, waittime.tv_sec);
934 waittime.tv_nsec = 0;
935 }
936 }
937
938 if (SCM_TYP16 (cv) == scm_tc16_fair_condvar)
939 err = fair_cond_timedwait (SCM_CONDVAR_DATA (cv),
940 SCM_MUTEX_DATA (mx),
941 SCM_UNBNDP (t) ? NULL : &waittime);
942 else
943 {
944 scm_t_cond *c = SCM_CONDVAR_DATA (cv);
945 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
946 if (SCM_UNBNDP (t))
947 err = scm_cond_wait (c, m);
948 else
949 err = scm_cond_timedwait (c, m, &waittime);
950 }
951
952 if (err)
953 {
954 if (err == ETIMEDOUT)
955 return SCM_BOOL_F;
956 errno = err;
957 SCM_SYSERROR;
958 }
959 return SCM_BOOL_T;
960 }
961 #undef FUNC_NAME
962
963 SCM_DEFINE (scm_signal_condition_variable, "signal-condition-variable", 1, 0, 0,
964 (SCM cv),
965 "Wake up one thread that is waiting for @var{cv}")
966 #define FUNC_NAME s_scm_signal_condition_variable
967 {
968 SCM_VALIDATE_CONDVAR (1, cv);
969 if (SCM_TYP16 (cv) == scm_tc16_fair_condvar)
970 fair_cond_signal (SCM_CONDVAR_DATA (cv));
971 else
972 {
973 scm_t_cond *c = SCM_CONDVAR_DATA (cv);
974 scm_cond_signal (c);
975 }
976 return SCM_BOOL_T;
977 }
978 #undef FUNC_NAME
979
980 SCM_DEFINE (scm_broadcast_condition_variable, "broadcast-condition-variable", 1, 0, 0,
981 (SCM cv),
982 "Wake up all threads that are waiting for @var{cv}. ")
983 #define FUNC_NAME s_scm_broadcast_condition_variable
984 {
985 SCM_VALIDATE_CONDVAR (1, cv);
986 if (SCM_TYP16 (cv) == scm_tc16_fair_condvar)
987 fair_cond_broadcast (SCM_CONDVAR_DATA (cv));
988 else
989 {
990 scm_t_cond *c = SCM_CONDVAR_DATA (cv);
991 scm_cond_broadcast (c);
992 }
993 return SCM_BOOL_T;
994 }
995 #undef FUNC_NAME
996
997 /*** Marking stacks */
998
999 /* XXX - what to do with this? Do we need to handle this for blocked
1000 threads as well?
1001 */
1002 #ifdef __ia64__
1003 # define SCM_MARK_BACKING_STORE() do { \
1004 ucontext_t ctx; \
1005 SCM_STACKITEM * top, * bot; \
1006 getcontext (&ctx); \
1007 scm_mark_locations ((SCM_STACKITEM *) &ctx.uc_mcontext, \
1008 ((size_t) (sizeof (SCM_STACKITEM) - 1 + sizeof ctx.uc_mcontext) \
1009 / sizeof (SCM_STACKITEM))); \
1010 bot = (SCM_STACKITEM *) __libc_ia64_register_backing_store_base; \
1011 top = (SCM_STACKITEM *) ctx.uc_mcontext.sc_ar_bsp; \
1012 scm_mark_locations (bot, top - bot); } while (0)
1013 #else
1014 # define SCM_MARK_BACKING_STORE()
1015 #endif
1016
1017 void
1018 scm_threads_mark_stacks (void)
1019 {
1020 volatile SCM c;
1021 for (c = all_threads; !SCM_NULLP (c); c = SCM_CDR (c))
1022 {
1023 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (c));
1024 if (!THREAD_INITIALIZED_P (t))
1025 {
1026 /* Not fully initialized yet. */
1027 continue;
1028 }
1029 if (t->top == NULL)
1030 {
1031 long stack_len;
1032 #ifdef SCM_DEBUG
1033 if (t->thread != scm_thread_self ())
1034 abort ();
1035 #endif
1036 /* Active thread */
1037 /* stack_len is long rather than sizet in order to guarantee
1038 that &stack_len is long aligned */
1039 #ifdef STACK_GROWS_UP
1040 stack_len = ((SCM_STACKITEM *) (&t) -
1041 (SCM_STACKITEM *) thread->base);
1042
1043 /* Protect from the C stack. This must be the first marking
1044 * done because it provides information about what objects
1045 * are "in-use" by the C code. "in-use" objects are those
1046 * for which the information about length and base address must
1047 * remain usable. This requirement is stricter than a liveness
1048 * requirement -- in particular, it constrains the implementation
1049 * of scm_resizuve.
1050 */
1051 SCM_FLUSH_REGISTER_WINDOWS;
1052 /* This assumes that all registers are saved into the jmp_buf */
1053 setjmp (scm_save_regs_gc_mark);
1054 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
1055 ((size_t) sizeof scm_save_regs_gc_mark
1056 / sizeof (SCM_STACKITEM)));
1057
1058 scm_mark_locations (((size_t) t->base,
1059 (sizet) stack_len));
1060 #else
1061 stack_len = ((SCM_STACKITEM *) t->base -
1062 (SCM_STACKITEM *) (&t));
1063
1064 /* Protect from the C stack. This must be the first marking
1065 * done because it provides information about what objects
1066 * are "in-use" by the C code. "in-use" objects are those
1067 * for which the information about length and base address must
1068 * remain usable. This requirement is stricter than a liveness
1069 * requirement -- in particular, it constrains the implementation
1070 * of scm_resizuve.
1071 */
1072 SCM_FLUSH_REGISTER_WINDOWS;
1073 /* This assumes that all registers are saved into the jmp_buf */
1074 setjmp (scm_save_regs_gc_mark);
1075 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
1076 ((size_t) sizeof scm_save_regs_gc_mark
1077 / sizeof (SCM_STACKITEM)));
1078
1079 scm_mark_locations ((SCM_STACKITEM *) &t,
1080 stack_len);
1081 #endif
1082 }
1083 else
1084 {
1085 /* Suspended thread */
1086 #ifdef STACK_GROWS_UP
1087 long stack_len = t->top - t->base;
1088 scm_mark_locations (t->base, stack_len);
1089 #else
1090 long stack_len = t->base - t->top;
1091 scm_mark_locations (t->top, stack_len);
1092 #endif
1093 scm_mark_locations ((SCM_STACKITEM *) t->regs,
1094 ((size_t) sizeof(t->regs)
1095 / sizeof (SCM_STACKITEM)));
1096 }
1097 }
1098 }
1099
1100 /*** Select */
1101
1102 int
1103 scm_internal_select (int nfds,
1104 SELECT_TYPE *readfds,
1105 SELECT_TYPE *writefds,
1106 SELECT_TYPE *exceptfds,
1107 struct timeval *timeout)
1108 {
1109 int res, eno;
1110 scm_thread *c = scm_i_leave_guile ();
1111 res = scm_i_plugin_select (nfds, readfds, writefds, exceptfds, timeout);
1112 eno = errno;
1113 scm_i_enter_guile (c);
1114 SCM_ASYNC_TICK;
1115 errno = eno;
1116 return res;
1117 }
1118
1119 /* Low-level C API */
1120
1121 SCM
1122 scm_spawn_thread (scm_t_catch_body body, void *body_data,
1123 scm_t_catch_handler handler, void *handler_data)
1124 {
1125 return create_thread (body, body_data, handler, handler_data, SCM_BOOL_F);
1126 }
1127
1128 int
1129 scm_mutex_lock (scm_t_mutex *m)
1130 {
1131 scm_thread *t = scm_i_leave_guile ();
1132 int res = scm_i_plugin_mutex_lock (m);
1133 scm_i_enter_guile (t);
1134 return res;
1135 }
1136
1137 scm_t_rec_mutex *
1138 scm_make_rec_mutex ()
1139 {
1140 scm_t_rec_mutex *m = scm_malloc (sizeof (scm_t_rec_mutex));
1141 scm_i_plugin_rec_mutex_init (m, &scm_i_plugin_rec_mutex);
1142 return m;
1143 }
1144
1145 void
1146 scm_rec_mutex_free (scm_t_rec_mutex *m)
1147 {
1148 scm_i_plugin_rec_mutex_destroy (m);
1149 free (m);
1150 }
1151
1152 int
1153 scm_rec_mutex_lock (scm_t_rec_mutex *m)
1154 {
1155 scm_thread *t = scm_i_leave_guile ();
1156 int res = scm_i_plugin_rec_mutex_lock (m);
1157 scm_i_enter_guile (t);
1158 return res;
1159 }
1160
1161 int
1162 scm_cond_wait (scm_t_cond *c, scm_t_mutex *m)
1163 {
1164 scm_thread *t = scm_i_leave_guile ();
1165 scm_i_plugin_cond_wait (c, m);
1166 scm_i_enter_guile (t);
1167 return 0;
1168 }
1169
1170 int
1171 scm_cond_timedwait (scm_t_cond *c, scm_t_mutex *m, const struct timespec *wt)
1172 {
1173 scm_thread *t = scm_i_leave_guile ();
1174 int res = scm_i_plugin_cond_timedwait (c, m, wt);
1175 scm_i_enter_guile (t);
1176 return res;
1177 }
1178
1179 void
1180 scm_enter_guile ()
1181 {
1182 scm_i_enter_guile (SCM_CURRENT_THREAD);
1183 }
1184
1185 void
1186 scm_leave_guile ()
1187 {
1188 scm_i_leave_guile ();
1189 }
1190
1191 unsigned long
1192 scm_thread_usleep (unsigned long usecs)
1193 {
1194 struct timeval tv;
1195 tv.tv_usec = usecs % 1000000;
1196 tv.tv_sec = usecs / 1000000;
1197 scm_internal_select (0, NULL, NULL, NULL, &tv);
1198 return tv.tv_usec + tv.tv_sec*1000000;
1199 }
1200
1201 unsigned long
1202 scm_thread_sleep (unsigned long secs)
1203 {
1204 struct timeval tv;
1205 tv.tv_usec = 0;
1206 tv.tv_sec = secs;
1207 scm_internal_select (0, NULL, NULL, NULL, &tv);
1208 return tv.tv_sec;
1209 }
1210
1211 /*** Misc */
1212
1213 SCM_DEFINE (scm_current_thread, "current-thread", 0, 0, 0,
1214 (void),
1215 "Return the thread that called this function.")
1216 #define FUNC_NAME s_scm_current_thread
1217 {
1218 return cur_thread;
1219 }
1220 #undef FUNC_NAME
1221
1222 SCM_DEFINE (scm_all_threads, "all-threads", 0, 0, 0,
1223 (void),
1224 "Return a list of all threads.")
1225 #define FUNC_NAME s_scm_all_threads
1226 {
1227 return scm_list_copy (all_threads);
1228 }
1229 #undef FUNC_NAME
1230
1231 scm_root_state *
1232 scm_i_thread_root (SCM thread)
1233 {
1234 return ((scm_thread *) SCM_THREAD_DATA (thread))->root;
1235 }
1236
1237 SCM_DEFINE (scm_thread_exited_p, "thread-exited?", 1, 0, 0,
1238 (SCM thread),
1239 "Return @code{#t} iff @var{thread} has exited.\n")
1240 #define FUNC_NAME s_scm_thread_exited_p
1241 {
1242 return SCM_BOOL (scm_c_thread_exited_p (thread));
1243 }
1244 #undef FUNC_NAME
1245
1246 int
1247 scm_c_thread_exited_p (SCM thread)
1248 #define FUNC_NAME s_scm_thread_exited_p
1249 {
1250 scm_thread *t;
1251 SCM_VALIDATE_THREAD (1, thread);
1252 t = SCM_THREAD_DATA (thread);
1253 return t->exited;
1254 }
1255 #undef FUNC_NAME
1256
1257 static scm_t_cond wake_up_cond;
1258 int scm_i_thread_go_to_sleep;
1259 static int gc_section_count = 0;
1260 static int threads_initialized_p = 0;
1261
1262 void
1263 scm_i_thread_put_to_sleep ()
1264 {
1265 if (threads_initialized_p && !gc_section_count++)
1266 {
1267 SCM threads;
1268 scm_i_plugin_mutex_lock (&thread_admin_mutex);
1269 threads = all_threads;
1270 /* Signal all threads to go to sleep */
1271 scm_i_thread_go_to_sleep = 1;
1272 for (; !SCM_NULLP (threads); threads = SCM_CDR (threads))
1273 if (SCM_CAR (threads) != cur_thread)
1274 {
1275 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (threads));
1276 scm_i_plugin_mutex_lock (&t->heap_mutex);
1277 }
1278 scm_i_thread_go_to_sleep = 0;
1279 }
1280 }
1281
1282 void
1283 scm_i_thread_invalidate_freelists ()
1284 {
1285 /* Don't need to lock thread_admin_mutex here since we are single threaded */
1286 SCM threads = all_threads;
1287 for (; !SCM_NULLP (threads); threads = SCM_CDR (threads))
1288 if (SCM_CAR (threads) != cur_thread)
1289 {
1290 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (threads));
1291 t->clear_freelists_p = 1;
1292 }
1293 }
1294
1295 void
1296 scm_i_thread_wake_up ()
1297 {
1298 if (threads_initialized_p && !--gc_section_count)
1299 {
1300 SCM threads;
1301 threads = all_threads;
1302 scm_i_plugin_cond_broadcast (&wake_up_cond);
1303 for (; !SCM_NULLP (threads); threads = SCM_CDR (threads))
1304 if (SCM_CAR (threads) != cur_thread)
1305 {
1306 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (threads));
1307 scm_i_plugin_mutex_unlock (&t->heap_mutex);
1308 }
1309 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
1310 }
1311 }
1312
1313 void
1314 scm_i_thread_sleep_for_gc ()
1315 {
1316 scm_thread *t;
1317 t = suspend ();
1318 scm_i_plugin_cond_wait (&wake_up_cond, &t->heap_mutex);
1319 resume (t);
1320 }
1321
1322 scm_t_mutex scm_i_critical_section_mutex;
1323 scm_t_rec_mutex scm_i_defer_mutex;
1324
1325 #ifdef USE_PTHREAD_THREADS
1326 #include "libguile/pthread-threads.c"
1327 #endif
1328 #include "libguile/threads-plugin.c"
1329
1330 /*** Initialization */
1331
1332 void
1333 scm_threads_prehistory ()
1334 {
1335 scm_thread *t;
1336 #ifdef USE_PTHREAD_THREADS
1337 /* Must be called before any initialization of a mutex. */
1338 scm_init_pthread_threads ();
1339 #endif
1340 scm_i_plugin_mutex_init (&thread_admin_mutex, &scm_i_plugin_mutex);
1341 scm_i_plugin_cond_init (&wake_up_cond, 0);
1342 scm_i_plugin_mutex_init (&scm_i_critical_section_mutex, &scm_i_plugin_mutex);
1343 thread_count = 1;
1344 scm_i_plugin_key_create (&scm_i_thread_key, 0);
1345 scm_i_plugin_key_create (&scm_i_root_state_key, 0);
1346 scm_i_plugin_rec_mutex_init (&scm_i_defer_mutex, &scm_i_plugin_rec_mutex);
1347 /* Allocate a fake thread object to be used during bootup. */
1348 t = malloc (sizeof (scm_thread));
1349 t->base = NULL;
1350 t->clear_freelists_p = 0;
1351 scm_i_plugin_mutex_init (&t->heap_mutex, &scm_i_plugin_mutex);
1352 scm_setspecific (scm_i_thread_key, t);
1353 scm_i_enter_guile (t);
1354 }
1355
1356 scm_t_bits scm_tc16_thread;
1357 scm_t_bits scm_tc16_future;
1358 scm_t_bits scm_tc16_mutex;
1359 scm_t_bits scm_tc16_fair_mutex;
1360 scm_t_bits scm_tc16_condvar;
1361 scm_t_bits scm_tc16_fair_condvar;
1362
1363 void
1364 scm_init_threads (SCM_STACKITEM *base)
1365 {
1366 SCM thread;
1367 scm_tc16_thread = scm_make_smob_type ("thread", sizeof (scm_thread));
1368 scm_tc16_mutex = scm_make_smob_type ("mutex", sizeof (scm_t_mutex));
1369 scm_tc16_fair_mutex = scm_make_smob_type ("fair-mutex",
1370 sizeof (fair_mutex));
1371 scm_tc16_condvar = scm_make_smob_type ("condition-variable",
1372 sizeof (scm_t_cond));
1373 scm_tc16_fair_condvar = scm_make_smob_type ("fair-condition-variable",
1374 sizeof (fair_cond));
1375
1376 thread = make_thread (SCM_BOOL_F);
1377 /* Replace initial fake thread with a real thread object */
1378 free (SCM_CURRENT_THREAD);
1379 scm_setspecific (scm_i_thread_key, SCM_THREAD_DATA (thread));
1380 scm_i_enter_guile (SCM_CURRENT_THREAD);
1381
1382 /* root is set later from init.c */
1383 init_thread_creatant (thread, base);
1384 thread_count = 1;
1385 scm_gc_register_root (&all_threads);
1386 all_threads = scm_cons (thread, SCM_EOL);
1387
1388 scm_set_smob_mark (scm_tc16_thread, thread_mark);
1389 scm_set_smob_print (scm_tc16_thread, thread_print);
1390 scm_set_smob_free (scm_tc16_thread, thread_free);
1391
1392 scm_set_smob_mark (scm_tc16_fair_mutex, fair_mutex_mark);
1393
1394 scm_set_smob_mark (scm_tc16_fair_condvar, fair_cond_mark);
1395
1396 scm_tc16_future = scm_make_smob_type ("future", 0);
1397 scm_set_smob_mark (scm_tc16_future, scm_markcdr);
1398 scm_set_smob_free (scm_tc16_future, future_free);
1399 scm_set_smob_print (scm_tc16_future, future_print);
1400
1401 threads_initialized_p = 1;
1402 }
1403
1404 void
1405 scm_init_thread_procs ()
1406 {
1407 scm_loc_sys_thread_handler
1408 = SCM_VARIABLE_LOC (scm_c_define ("%thread-handler", SCM_BOOL_F));
1409 #include "libguile/threads.x"
1410 }
1411
1412 /* XXX */
1413
1414 void
1415 scm_init_iselect ()
1416 {
1417 }
1418
1419 /*
1420 Local Variables:
1421 c-file-style: "gnu"
1422 End:
1423 */