* threads.c (really_launch): Detach before unlocking
[bpt/guile.git] / libguile / threads.c
CommitLineData
d823b11b 1/* Copyright (C) 1995,1996,1997,1998,2000,2001, 2002 Free Software Foundation, Inc.
7bfd3b9e
JB
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
82892bed
JB
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
7bfd3b9e
JB
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.
82892bed 40 * If you do not wish that, delete this exception notice. */
1bbd0b84 41
1bbd0b84 42
7bfd3b9e
JB
43\f
44
d823b11b
MV
45/* This file implements nice Scheme level threads on top of the gastly
46 C level threads.
7f0f3eaa
JB
47*/
48
d823b11b
MV
49#include <unistd.h>
50#include <stdio.h>
51#include <assert.h>
52#include <sys/time.h>
5f05c406 53
a0599745 54#include "libguile/_scm.h"
d823b11b
MV
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"
a0599745 61#include "libguile/dynwind.h"
d823b11b 62#include "libguile/iselect.h"
7bfd3b9e 63
d823b11b 64/*** Queues */
7bfd3b9e 65
d823b11b
MV
66static SCM
67make_queue ()
68{
69 return scm_cons (SCM_EOL, SCM_EOL);
70}
7bfd3b9e 71
d823b11b
MV
72static SCM
73enqueue (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}
7bfd3b9e 83
d823b11b
MV
84static void
85remqueue (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
102static SCM
103dequeue (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}
7bfd3b9e 116
d823b11b
MV
117/*** Threads */
118
9bc4701c
MD
119#define THREAD_INITIALIZED_P(t) (t->base != NULL)
120
121struct scm_thread {
d823b11b
MV
122
123 /* Blocking.
124 */
125 scm_t_cond sleep_cond;
126 struct scm_thread *next_waiting;
127
9bc4701c
MD
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
d823b11b
MV
133 scm_root_state *root;
134 SCM handle;
135 scm_t_thread thread;
136 SCM result;
137 int exited;
138
d823b11b
MV
139 /* For keeping track of the stack and registers. */
140 SCM_STACKITEM *base;
141 SCM_STACKITEM *top;
142 jmp_buf regs;
143
9bc4701c 144};
d823b11b
MV
145
146static SCM
147make_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;
9bc4701c 156 scm_i_plugin_cond_init (&t->sleep_cond, 0);
dea5539e 157 scm_i_plugin_mutex_init (&t->heap_mutex, &scm_i_plugin_mutex);
9bc4701c 158 t->clear_freelists_p = 0;
d823b11b
MV
159 t->exited = 0;
160 return z;
161}
162
163static void
9bc4701c
MD
164init_thread_creatant (SCM thread,
165 SCM_STACKITEM *base)
d823b11b 166{
9bc4701c
MD
167 scm_thread *t = SCM_THREAD_DATA (thread);
168 t->thread = scm_thread_self ();
d823b11b
MV
169 t->base = base;
170 t->top = NULL;
171}
4079f87e 172
d823b11b
MV
173static SCM
174thread_mark (SCM obj)
175{
176 scm_thread *t = SCM_THREAD_DATA (obj);
177 scm_gc_mark (t->result);
9bc4701c 178 return t->root->handle; /* mark root-state of this thread */
d823b11b
MV
179}
180
181static int
182thread_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);
1b92fb6b
MD
186 scm_intprint ((unsigned long)t->thread, 10, port);
187 scm_puts (" (", port);
d823b11b 188 scm_intprint ((unsigned long)t, 16, port);
1b92fb6b 189 scm_puts (")>", port);
d823b11b
MV
190 return 1;
191}
192
193static size_t
194thread_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
d823b11b 203/*** Scheduling */
f7eca35d 204
9bc4701c
MD
205#define cur_thread (SCM_CURRENT_THREAD->handle)
206scm_t_key scm_i_thread_key;
207scm_t_key scm_i_root_state_key;
d823b11b
MV
208
209void
210scm_i_set_thread_data (void *data)
211{
9bc4701c
MD
212 scm_thread *t = SCM_CURRENT_THREAD;
213 scm_setspecific (scm_i_root_state_key, data);
d823b11b
MV
214 t->root = (scm_root_state *)data;
215}
216
217static void
218resume (scm_thread *t)
219{
d823b11b 220 t->top = NULL;
9bc4701c
MD
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 }
d823b11b
MV
227}
228
9bc4701c
MD
229void
230scm_i_enter_guile (scm_thread *t)
d823b11b 231{
9bc4701c 232 scm_i_plugin_mutex_lock (&t->heap_mutex);
d823b11b
MV
233 resume (t);
234}
235
236static scm_thread *
237suspend ()
238{
9bc4701c 239 scm_thread *c = SCM_CURRENT_THREAD;
d823b11b
MV
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
9bc4701c
MD
250scm_thread *
251scm_i_leave_guile ()
d823b11b 252{
9bc4701c
MD
253 scm_thread *t = suspend ();
254 scm_i_plugin_mutex_unlock (&t->heap_mutex);
255 return t;
d823b11b
MV
256}
257
258/* Put the current thread to sleep until it is explicitely unblocked.
259 */
260static int
261block ()
262{
263 int err;
264 scm_thread *t = suspend ();
9bc4701c 265 err = scm_i_plugin_cond_wait (&t->sleep_cond, &t->heap_mutex);
d823b11b
MV
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 */
274static int
9bc4701c 275timed_block (const struct timespec *at)
d823b11b
MV
276{
277 int err;
278 scm_thread *t = suspend ();
9bc4701c 279 err = scm_i_plugin_cond_timedwait (&t->sleep_cond, &t->heap_mutex, at);
d823b11b
MV
280 resume (t);
281 return err;
282}
283
284/* Unblock a sleeping thread.
285 */
286static void
287unblock (scm_thread *t)
288{
9bc4701c 289 scm_i_plugin_cond_signal (&t->sleep_cond);
d823b11b
MV
290}
291
292/*** Thread creation */
293
9bc4701c 294static scm_t_mutex thread_admin_mutex;
d823b11b
MV
295static SCM all_threads;
296static int thread_count;
297
298typedef 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
307static SCM
308body_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
315static SCM
316handler_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
322static void
323really_launch (SCM_STACKITEM *base, launch_data *data)
324{
9bc4701c
MD
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
d823b11b
MV
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);
094b640d 342 scm_i_leave_guile (); /* release the heap */
d823b11b
MV
343 free (data);
344
9bc4701c
MD
345 scm_i_plugin_mutex_lock (&thread_admin_mutex);
346 all_threads = scm_delq_x (thread, all_threads);
d823b11b
MV
347 t->exited = 1;
348 thread_count--;
e29e0b09 349 /* detach before unlocking in order to not become joined when detached */
c4c52ebe 350 scm_thread_detach (t->thread);
e29e0b09 351 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
d823b11b
MV
352}
353
9bc4701c 354static void *
d823b11b
MV
355launch_thread (void *p)
356{
357 really_launch ((SCM_STACKITEM *)&p, (launch_data *)p);
9bc4701c 358 return 0;
d823b11b
MV
359}
360
361static SCM
362create_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;
c4c52ebe 376 SCM root, old_winds;
d823b11b 377 launch_data *data;
9bc4701c 378 scm_thread *t;
d823b11b
MV
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;
9bc4701c
MD
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. */
c4c52ebe
MD
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);
392d2833
MD
412
413 scm_remember_upto_here_1 (root);
414
c4c52ebe
MD
415 scm_i_enter_guile (parent);
416 }
9bc4701c
MD
417
418 err = scm_i_plugin_thread_create (&th, 0, launch_thread, (void *) data);
419 if (err != 0)
d823b11b 420 {
9bc4701c
MD
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);
d823b11b 426 }
d823b11b
MV
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
441SCM_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
d823b11b 463SCM_DEFINE (scm_join_thread, "join-thread", 1, 0, 0,
5f05c406 464 (SCM thread),
d823b11b
MV
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
5f05c406 468{
d823b11b
MV
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 {
0b6843b1
MD
479 scm_thread *c;
480 c = scm_i_leave_guile ();
9bc4701c 481 while (!THREAD_INITIALIZED_P (t))
0b6843b1 482 scm_i_plugin_thread_yield ();
9bc4701c
MD
483 scm_thread_join (t->thread, 0);
484 scm_i_enter_guile (c);
d823b11b
MV
485 }
486 res = t->result;
487 t->result = SCM_BOOL_F;
488 return res;
5f05c406
MV
489}
490#undef FUNC_NAME
491
28d52ebb
MD
492SCM *scm_loc_sys_thread_handler;
493
494SCM
495scm_i_make_future (SCM thunk)
496{
497 SCM_RETURN_NEWSMOB2 (scm_tc16_future,
498 create_thread ((scm_t_catch_body) scm_call_0,
499 thunk,
500 (scm_t_catch_handler) scm_apply_1,
501 *scm_loc_sys_thread_handler,
502 scm_cons (thunk,
503 *scm_loc_sys_thread_handler)),
504 scm_make_rec_mutex ());
505}
506
507static size_t
508future_free (SCM future)
509{
510 scm_rec_mutex_free (SCM_FUTURE_MUTEX (future));
511 return 0;
512}
513
514static int
515future_print (SCM exp, SCM port, scm_print_state *pstate)
516{
517 int writingp = SCM_WRITINGP (pstate);
518 scm_puts ("#<future ", port);
519 SCM_SET_WRITINGP (pstate, 1);
520 scm_iprin1 (SCM_FUTURE_DATA (exp), port, pstate);
521 SCM_SET_WRITINGP (pstate, writingp);
522 scm_putc ('>', port);
523 return !0;
524}
525
526SCM_DEFINE (scm_future_ref, "future-ref", 1, 0, 0,
527 (SCM future),
528 "If the future @var{x} has not been computed yet, compute and\n"
529 "return @var{x}, otherwise just return the previously computed\n"
530 "value.")
531#define FUNC_NAME s_scm_future_ref
532{
533 SCM_VALIDATE_FUTURE (1, future);
534 scm_rec_mutex_lock (SCM_FUTURE_MUTEX (future));
535 if (!SCM_FUTURE_COMPUTED_P (future))
536 {
537 SCM value = scm_join_thread (SCM_FUTURE_DATA (future));
538 if (!SCM_FUTURE_COMPUTED_P (future))
539 {
540 SCM_SET_FUTURE_DATA (future, value);
541 SCM_SET_FUTURE_COMPUTED (future);
542 }
543 }
544 scm_rec_mutex_unlock (SCM_FUTURE_MUTEX (future));
545 return SCM_FUTURE_DATA (future);
546}
547#undef FUNC_NAME
548
9bc4701c 549/*** Fair mutexes */
4079f87e 550
d823b11b
MV
551/* We implement our own mutex type since we want them to be 'fair', we
552 want to do fancy things while waiting for them (like running
553 asyncs) and we want to support waiting on many things at once.
554 Also, we might add things that are nice for debugging.
555*/
4079f87e 556
9bc4701c 557typedef struct fair_mutex {
d823b11b 558 /* the thread currently owning the mutex, or SCM_BOOL_F. */
9bc4701c
MD
559 scm_t_mutex lock;
560 int lockedp;
d823b11b
MV
561 SCM owner;
562 /* how much the owner owns us. */
563 int level;
564 /* the threads waiting for this mutex. */
565 SCM waiting;
9bc4701c 566} fair_mutex;
5f05c406 567
d823b11b 568static SCM
9bc4701c 569fair_mutex_mark (SCM mx)
d823b11b 570{
9bc4701c 571 fair_mutex *m = SCM_MUTEX_DATA (mx);
d823b11b
MV
572 scm_gc_mark (m->owner);
573 return m->waiting;
574}
4079f87e 575
9bc4701c 576SCM_DEFINE (scm_make_fair_mutex, "make-fair-mutex", 0, 0, 0,
d823b11b 577 (void),
9bc4701c
MD
578 "Create a new fair mutex object. ")
579#define FUNC_NAME s_scm_make_fair_mutex
d823b11b 580{
9bc4701c
MD
581 SCM mx = scm_make_smob (scm_tc16_fair_mutex);
582 fair_mutex *m = SCM_MUTEX_DATA (mx);
dea5539e 583 scm_i_plugin_mutex_init (&m->lock, &scm_i_plugin_mutex);
9bc4701c 584 m->lockedp = 0;
d823b11b
MV
585 m->owner = SCM_BOOL_F;
586 m->level = 0;
587 m->waiting = make_queue ();
588 return mx;
589}
590#undef FUNC_NAME
4079f87e 591
9bc4701c
MD
592static int
593fair_mutex_lock (fair_mutex *m)
d823b11b 594{
9bc4701c
MD
595 scm_i_plugin_mutex_lock (&m->lock);
596#if 0
597 /* Need to wait if another thread is just temporarily unlocking.
598 This is happens very seldom and only when the other thread is
599 between scm_mutex_unlock and scm_i_plugin_mutex_lock below. */
600 while (m->lockedp)
601 SCM_TICK;
602 m->lockedp = 1;
603#endif
604
d823b11b
MV
605 if (m->owner == SCM_BOOL_F)
606 m->owner = cur_thread;
607 else if (m->owner == cur_thread)
608 m->level++;
609 else
610 {
611 while (1)
612 {
613 SCM c = enqueue (m->waiting, cur_thread);
9bc4701c
MD
614 int err;
615 /* Note: It's important that m->lock is never locked for
616 any longer amount of time since that could prevent GC */
617 scm_i_plugin_mutex_unlock (&m->lock);
618 err = block ();
d823b11b 619 if (m->owner == cur_thread)
9bc4701c
MD
620 return 0;
621 scm_i_plugin_mutex_lock (&m->lock);
d823b11b 622 remqueue (m->waiting, c);
9bc4701c 623 scm_i_plugin_mutex_unlock (&m->lock);
d823b11b 624 if (err)
9bc4701c 625 return err;
d823b11b 626 SCM_ASYNC_TICK;
9bc4701c 627 scm_i_plugin_mutex_lock (&m->lock);
d823b11b
MV
628 }
629 }
9bc4701c
MD
630 scm_i_plugin_mutex_unlock (&m->lock);
631 return 0;
d823b11b 632}
7bfd3b9e 633
9bc4701c
MD
634static int
635fair_mutex_trylock (fair_mutex *m)
d823b11b 636{
9bc4701c 637 scm_i_plugin_mutex_lock (&m->lock);
d823b11b
MV
638 if (m->owner == SCM_BOOL_F)
639 m->owner = cur_thread;
640 else if (m->owner == cur_thread)
641 m->level++;
642 else
9bc4701c
MD
643 {
644 scm_i_plugin_mutex_unlock (&m->lock);
645 return EBUSY;
646 }
647 scm_i_plugin_mutex_unlock (&m->lock);
648 return 0;
d823b11b 649}
d823b11b 650
9bc4701c
MD
651static int
652fair_mutex_unlock (fair_mutex *m)
5f05c406 653{
9bc4701c 654 scm_i_plugin_mutex_lock (&m->lock);
d823b11b
MV
655 if (m->owner != cur_thread)
656 {
9bc4701c
MD
657 scm_i_plugin_mutex_unlock (&m->lock);
658 return EPERM;
d823b11b
MV
659 }
660 else if (m->level > 0)
661 m->level--;
662 else
663 {
664 SCM next = dequeue (m->waiting);
665 if (!SCM_FALSEP (next))
666 {
667 m->owner = next;
668 unblock (SCM_THREAD_DATA (next));
d823b11b
MV
669 }
670 else
671 m->owner = SCM_BOOL_F;
672 }
9bc4701c
MD
673 scm_i_plugin_mutex_unlock (&m->lock);
674 return 0;
5f05c406
MV
675}
676
9bc4701c 677/*** Fair condition variables */
7bfd3b9e 678
d823b11b
MV
679/* Like mutexes, we implement our own condition variables using the
680 primitives above.
681*/
5f05c406 682
9bc4701c
MD
683typedef struct fair_cond {
684 scm_t_mutex lock;
d823b11b
MV
685 /* the threads waiting for this condition. */
686 SCM waiting;
9bc4701c 687} fair_cond;
5f05c406 688
d823b11b 689static SCM
9bc4701c 690fair_cond_mark (SCM cv)
5f05c406 691{
9bc4701c 692 fair_cond *c = SCM_CONDVAR_DATA (cv);
d823b11b 693 return c->waiting;
5f05c406
MV
694}
695
9bc4701c
MD
696SCM_DEFINE (scm_make_fair_condition_variable, "make-fair-condition-variable", 0, 0, 0,
697 (void),
698 "Make a new fair condition variable.")
699#define FUNC_NAME s_scm_make_fair_condition_variable
700{
701 SCM cv = scm_make_smob (scm_tc16_fair_condvar);
702 fair_cond *c = SCM_CONDVAR_DATA (cv);
703 scm_i_plugin_mutex_init (&c->lock, 0);
704 c->waiting = make_queue ();
705 return cv;
706}
707#undef FUNC_NAME
708
709static int
710fair_cond_timedwait (fair_cond *c,
711 fair_mutex *m,
712 const struct timespec *waittime)
713{
714 int err;
715 scm_i_plugin_mutex_lock (&c->lock);
716
717 while (1)
718 {
719 enqueue (c->waiting, cur_thread);
720 scm_i_plugin_mutex_unlock (&c->lock);
721 fair_mutex_unlock (m); /*fixme* - not thread safe */
722 if (waittime == NULL)
723 err = block ();
724 else
725 err = timed_block (waittime);
726 fair_mutex_lock (m);
727 if (err)
728 return err;
729 /* XXX - check whether we have been signalled. */
730 break;
731 }
732 return err;
733}
734
735static int
736fair_cond_signal (fair_cond *c)
737{
738 SCM th;
739 scm_i_plugin_mutex_lock (&c->lock);
740 if (!SCM_FALSEP (th = dequeue (c->waiting)))
741 unblock (SCM_THREAD_DATA (th));
742 scm_i_plugin_mutex_unlock (&c->lock);
743 return 0;
744}
745
746static int
747fair_cond_broadcast (fair_cond *c)
748{
749 SCM th;
750 scm_i_plugin_mutex_lock (&c->lock);
751 while (!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/*** Mutexes */
758
759SCM_DEFINE (scm_make_mutex, "make-mutex", 0, 0, 0,
760 (void),
761 "Create a new mutex object. ")
762#define FUNC_NAME s_scm_make_mutex
763{
764 SCM mx = scm_make_smob (scm_tc16_mutex);
dea5539e 765 scm_i_plugin_mutex_init (SCM_MUTEX_DATA (mx), &scm_i_plugin_mutex);
9bc4701c
MD
766 return mx;
767}
768#undef FUNC_NAME
769
770/*fixme* change documentation */
771SCM_DEFINE (scm_lock_mutex, "lock-mutex", 1, 0, 0,
772 (SCM mx),
773"Lock @var{mutex}. If the mutex is already locked, the calling thread "
774"blocks until the mutex becomes available. The function returns when "
775"the calling thread owns the lock on @var{mutex}. Locking a mutex that "
776"a thread already owns will succeed right away and will not block the "
777"thread. That is, Guile's mutexes are @emph{recursive}. ")
778#define FUNC_NAME s_scm_lock_mutex
779{
780 int err;
781 SCM_VALIDATE_MUTEX (1, mx);
782
783 if (SCM_TYP16 (mx) == scm_tc16_fair_mutex)
784 err = fair_mutex_lock (SCM_MUTEX_DATA (mx));
785 else
786 {
787 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
0b6843b1 788 err = scm_mutex_lock (m);
9bc4701c
MD
789 }
790
791 if (err)
792 {
793 errno = err;
794 SCM_SYSERROR;
795 }
796 return SCM_BOOL_T;
797}
798#undef FUNC_NAME
799
800SCM_DEFINE (scm_try_mutex, "try-mutex", 1, 0, 0,
801 (SCM mx),
802"Try to lock @var{mutex}. If the mutex is already locked by someone "
803"else, return @code{#f}. Else lock the mutex and return @code{#t}. ")
804#define FUNC_NAME s_scm_try_mutex
805{
806 int err;
807 SCM_VALIDATE_MUTEX (1, mx);
808
809 if (SCM_TYP16 (mx) == scm_tc16_fair_mutex)
810 err = fair_mutex_trylock (SCM_MUTEX_DATA (mx));
811 else
812 {
813 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
0b6843b1 814 err = scm_mutex_trylock (m);
9bc4701c
MD
815 }
816
817 if (err == EBUSY)
818 return SCM_BOOL_F;
819
820 if (err)
821 {
822 errno = err;
823 SCM_SYSERROR;
824 }
825
826 return SCM_BOOL_T;
827}
828#undef FUNC_NAME
829
830SCM_DEFINE (scm_unlock_mutex, "unlock-mutex", 1, 0, 0,
831 (SCM mx),
832"Unlocks @var{mutex} if the calling thread owns the lock on "
833"@var{mutex}. Calling unlock-mutex on a mutex not owned by the current "
834"thread results in undefined behaviour. Once a mutex has been unlocked, "
835"one thread blocked on @var{mutex} is awakened and grabs the mutex "
836"lock. Every call to @code{lock-mutex} by this thread must be matched "
837"with a call to @code{unlock-mutex}. Only the last call to "
838"@code{unlock-mutex} will actually unlock the mutex. ")
839#define FUNC_NAME s_scm_unlock_mutex
840{
841 int err;
842 SCM_VALIDATE_MUTEX (1, mx);
843
844 if (SCM_TYP16 (mx) == scm_tc16_fair_mutex)
845 {
846 err = fair_mutex_unlock (SCM_MUTEX_DATA (mx));
847 if (err == EPERM)
848 {
849 fair_mutex *m = SCM_MUTEX_DATA (mx);
850 if (m->owner != cur_thread)
851 {
852 if (m->owner == SCM_BOOL_F)
853 SCM_MISC_ERROR ("mutex not locked", SCM_EOL);
854 else
855 SCM_MISC_ERROR ("mutex not locked by this thread", SCM_EOL);
856 }
857 }
858 }
859 else
860 {
861 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
0b6843b1 862 err = scm_mutex_unlock (m);
9bc4701c
MD
863 }
864
865 if (err)
866 {
867 errno = err;
868 SCM_SYSERROR;
869 }
870 return SCM_BOOL_T;
871}
872#undef FUNC_NAME
873
874/*** Condition variables */
875
d823b11b
MV
876SCM_DEFINE (scm_make_condition_variable, "make-condition-variable", 0, 0, 0,
877 (void),
878 "Make a new condition variable.")
879#define FUNC_NAME s_scm_make_condition_variable
5f05c406 880{
d823b11b 881 SCM cv = scm_make_smob (scm_tc16_condvar);
9bc4701c 882 scm_i_plugin_cond_init (SCM_CONDVAR_DATA (cv), 0);
d823b11b 883 return cv;
5f05c406 884}
d823b11b 885#undef FUNC_NAME
5f05c406 886
d823b11b
MV
887SCM_DEFINE (scm_timed_wait_condition_variable, "wait-condition-variable", 2, 1, 0,
888 (SCM cv, SCM mx, SCM t),
889"Wait until @var{cond-var} has been signalled. While waiting, "
890"@var{mutex} is atomically unlocked (as with @code{unlock-mutex}) and "
891"is locked again when this function returns. When @var{time} is given, "
892"it specifies a point in time where the waiting should be aborted. It "
893"can be either a integer as returned by @code{current-time} or a pair "
894"as returned by @code{gettimeofday}. When the waiting is aborted the "
895"mutex is locked and @code{#f} is returned. When the condition "
896"variable is in fact signalled, the mutex is also locked and @code{#t} "
897"is returned. ")
898#define FUNC_NAME s_scm_timed_wait_condition_variable
5f05c406 899{
d823b11b
MV
900 struct timespec waittime;
901 int err;
902
903 SCM_VALIDATE_CONDVAR (1, cv);
904 SCM_VALIDATE_MUTEX (2, mx);
9bc4701c
MD
905 if (!((SCM_TYP16 (cv) == scm_tc16_condvar
906 && SCM_TYP16 (mx) == scm_tc16_mutex)
907 || (SCM_TYP16 (cv) == scm_tc16_fair_condvar
908 && SCM_TYP16 (mx) == scm_tc16_fair_mutex)))
909 SCM_MISC_ERROR ("Condition variable and mutex are of different kinds.",
910 SCM_EOL);
911
d823b11b
MV
912 if (!SCM_UNBNDP (t))
913 {
914 if (SCM_CONSP (t))
915 {
9bc4701c
MD
916 SCM_VALIDATE_UINT_COPY (3, SCM_CAR (t), waittime.tv_sec);
917 SCM_VALIDATE_UINT_COPY (3, SCM_CDR (t), waittime.tv_nsec);
d823b11b
MV
918 waittime.tv_nsec *= 1000;
919 }
920 else
921 {
922 SCM_VALIDATE_UINT_COPY (3, t, waittime.tv_sec);
923 waittime.tv_nsec = 0;
924 }
925 }
926
9bc4701c
MD
927 if (SCM_TYP16 (cv) == scm_tc16_fair_condvar)
928 err = fair_cond_timedwait (SCM_CONDVAR_DATA (cv),
929 SCM_MUTEX_DATA (mx),
930 SCM_UNBNDP (t) ? NULL : &waittime);
931 else
932 {
933 scm_t_cond *c = SCM_CONDVAR_DATA (cv);
934 scm_t_mutex *m = SCM_MUTEX_DATA (mx);
0b6843b1 935 err = scm_cond_wait (c, m);
9bc4701c 936 }
d823b11b 937
9bc4701c 938 if (err)
d823b11b 939 {
9bc4701c
MD
940 errno = err;
941 SCM_SYSERROR;
d823b11b 942 }
9bc4701c 943 return SCM_BOOL_T;
5f05c406 944}
d823b11b 945#undef FUNC_NAME
5f05c406 946
d823b11b
MV
947SCM_DEFINE (scm_signal_condition_variable, "signal-condition-variable", 1, 0, 0,
948 (SCM cv),
949 "Wake up one thread that is waiting for @var{cv}")
950#define FUNC_NAME s_scm_signal_condition_variable
5f05c406 951{
d823b11b 952 SCM_VALIDATE_CONDVAR (1, cv);
9bc4701c
MD
953 if (SCM_TYP16 (cv) == scm_tc16_fair_condvar)
954 fair_cond_signal (SCM_CONDVAR_DATA (cv));
955 else
956 {
957 scm_t_cond *c = SCM_CONDVAR_DATA (cv);
0b6843b1 958 scm_cond_signal (c);
9bc4701c 959 }
d823b11b 960 return SCM_BOOL_T;
5f05c406 961}
d823b11b 962#undef FUNC_NAME
5f05c406 963
d823b11b
MV
964SCM_DEFINE (scm_broadcast_condition_variable, "broadcast-condition-variable", 1, 0, 0,
965 (SCM cv),
966 "Wake up all threads that are waiting for @var{cv}. ")
967#define FUNC_NAME s_scm_broadcast_condition_variable
5f05c406 968{
d823b11b 969 SCM_VALIDATE_CONDVAR (1, cv);
9bc4701c
MD
970 if (SCM_TYP16 (cv) == scm_tc16_fair_condvar)
971 fair_cond_broadcast (SCM_CONDVAR_DATA (cv));
972 else
973 {
974 scm_t_cond *c = SCM_CONDVAR_DATA (cv);
0b6843b1 975 scm_cond_broadcast (c);
9bc4701c 976 }
d823b11b 977 return SCM_BOOL_T;
5f05c406 978}
d823b11b 979#undef FUNC_NAME
5f05c406 980
d823b11b
MV
981/*** Marking stacks */
982
983/* XXX - what to do with this? Do we need to handle this for blocked
984 threads as well?
985*/
986#ifdef __ia64__
987# define SCM_MARK_BACKING_STORE() do { \
988 ucontext_t ctx; \
989 SCM_STACKITEM * top, * bot; \
990 getcontext (&ctx); \
991 scm_mark_locations ((SCM_STACKITEM *) &ctx.uc_mcontext, \
992 ((size_t) (sizeof (SCM_STACKITEM) - 1 + sizeof ctx.uc_mcontext) \
993 / sizeof (SCM_STACKITEM))); \
994 bot = (SCM_STACKITEM *) __libc_ia64_register_backing_store_base; \
995 top = (SCM_STACKITEM *) ctx.uc_mcontext.sc_ar_bsp; \
996 scm_mark_locations (bot, top - bot); } while (0)
997#else
998# define SCM_MARK_BACKING_STORE()
999#endif
1000
1001void
1002scm_threads_mark_stacks (void)
5f05c406 1003{
d823b11b
MV
1004 volatile SCM c;
1005 for (c = all_threads; !SCM_NULLP (c); c = SCM_CDR (c))
1006 {
1007 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (c));
9bc4701c 1008 if (!THREAD_INITIALIZED_P (t))
d823b11b
MV
1009 {
1010 /* Not fully initialized yet. */
1011 continue;
1012 }
1013 if (t->top == NULL)
1014 {
9bc4701c
MD
1015 long stack_len;
1016#ifdef SCM_DEBUG
1017 if (t->thread != scm_thread_self ())
1018 abort ();
1019#endif
d823b11b
MV
1020 /* Active thread */
1021 /* stack_len is long rather than sizet in order to guarantee
1022 that &stack_len is long aligned */
1023#ifdef STACK_GROWS_UP
9bc4701c
MD
1024 stack_len = ((SCM_STACKITEM *) (&t) -
1025 (SCM_STACKITEM *) thread->base);
d823b11b
MV
1026
1027 /* Protect from the C stack. This must be the first marking
1028 * done because it provides information about what objects
1029 * are "in-use" by the C code. "in-use" objects are those
1030 * for which the information about length and base address must
1031 * remain usable. This requirement is stricter than a liveness
1032 * requirement -- in particular, it constrains the implementation
1033 * of scm_resizuve.
1034 */
1035 SCM_FLUSH_REGISTER_WINDOWS;
1036 /* This assumes that all registers are saved into the jmp_buf */
1037 setjmp (scm_save_regs_gc_mark);
1038 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
1039 ((size_t) sizeof scm_save_regs_gc_mark
1040 / sizeof (SCM_STACKITEM)));
1041
1042 scm_mark_locations (((size_t) t->base,
1043 (sizet) stack_len));
1044#else
9bc4701c
MD
1045 stack_len = ((SCM_STACKITEM *) t->base -
1046 (SCM_STACKITEM *) (&t));
d823b11b
MV
1047
1048 /* Protect from the C stack. This must be the first marking
1049 * done because it provides information about what objects
1050 * are "in-use" by the C code. "in-use" objects are those
1051 * for which the information about length and base address must
1052 * remain usable. This requirement is stricter than a liveness
1053 * requirement -- in particular, it constrains the implementation
1054 * of scm_resizuve.
1055 */
1056 SCM_FLUSH_REGISTER_WINDOWS;
1057 /* This assumes that all registers are saved into the jmp_buf */
1058 setjmp (scm_save_regs_gc_mark);
1059 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
1060 ((size_t) sizeof scm_save_regs_gc_mark
1061 / sizeof (SCM_STACKITEM)));
1062
1063 scm_mark_locations ((SCM_STACKITEM *) &t,
1064 stack_len);
1065#endif
1066 }
1067 else
1068 {
1069 /* Suspended thread */
1070#ifdef STACK_GROWS_UP
1071 long stack_len = t->top - t->base;
1072 scm_mark_locations (t->base, stack_len);
1073#else
1074 long stack_len = t->base - t->top;
1075 scm_mark_locations (t->top, stack_len);
1076#endif
1077 scm_mark_locations ((SCM_STACKITEM *) t->regs,
1078 ((size_t) sizeof(t->regs)
1079 / sizeof (SCM_STACKITEM)));
1080 }
1081 }
5f05c406
MV
1082}
1083
d823b11b
MV
1084/*** Select */
1085
911782b7 1086int
d823b11b
MV
1087scm_internal_select (int nfds,
1088 SELECT_TYPE *readfds,
1089 SELECT_TYPE *writefds,
1090 SELECT_TYPE *exceptfds,
1091 struct timeval *timeout)
5f05c406 1092{
d823b11b 1093 int res, eno;
9bc4701c
MD
1094 scm_thread *c = scm_i_leave_guile ();
1095 res = scm_i_plugin_select (nfds, readfds, writefds, exceptfds, timeout);
d823b11b 1096 eno = errno;
9bc4701c 1097 scm_i_enter_guile (c);
d823b11b
MV
1098 SCM_ASYNC_TICK;
1099 errno = eno;
1100 return res;
5f05c406
MV
1101}
1102
9bc4701c
MD
1103/* Low-level C API */
1104
1105SCM
1106scm_spawn_thread (scm_t_catch_body body, void *body_data,
1107 scm_t_catch_handler handler, void *handler_data)
1108{
1109 return create_thread (body, body_data, handler, handler_data, SCM_BOOL_F);
1110}
1111
9bc4701c
MD
1112int
1113scm_mutex_lock (scm_t_mutex *m)
1114{
1115 scm_thread *t = scm_i_leave_guile ();
1116 int res = scm_i_plugin_mutex_lock (m);
1117 scm_i_enter_guile (t);
1118 return res;
1119}
1120
28d52ebb
MD
1121scm_t_rec_mutex *
1122scm_make_rec_mutex ()
1123{
1124 scm_t_rec_mutex *m = scm_malloc (sizeof (scm_t_rec_mutex));
1125 scm_i_plugin_rec_mutex_init (m, &scm_i_plugin_rec_mutex);
1126 return m;
1127}
1128
1129void
1130scm_rec_mutex_free (scm_t_rec_mutex *m)
1131{
1132 scm_i_plugin_rec_mutex_destroy (m);
1133 free (m);
1134}
1135
1136int
1137scm_rec_mutex_lock (scm_t_rec_mutex *m)
1138{
1139 scm_thread *t = scm_i_leave_guile ();
1140 int res = scm_i_plugin_rec_mutex_lock (m);
1141 scm_i_enter_guile (t);
1142 return res;
1143}
1144
9bc4701c
MD
1145int
1146scm_cond_wait (scm_t_cond *c, scm_t_mutex *m)
1147{
1148 scm_thread *t = scm_i_leave_guile ();
1149 scm_i_plugin_cond_wait (c, m);
1150 scm_i_enter_guile (t);
1151 return 0;
1152}
1153
1154int
06babecc 1155scm_cond_timedwait (scm_t_cond *c, scm_t_mutex *m, const struct timespec *wt)
9bc4701c
MD
1156{
1157 scm_thread *t = scm_i_leave_guile ();
06babecc 1158 int res = scm_i_plugin_cond_timedwait (c, m, wt);
9bc4701c
MD
1159 scm_i_enter_guile (t);
1160 return res;
1161}
9bc4701c
MD
1162
1163void
1164scm_enter_guile ()
1165{
1166 scm_i_enter_guile (SCM_CURRENT_THREAD);
1167}
1168
1169void
1170scm_leave_guile ()
1171{
1172 scm_i_leave_guile ();
1173}
1174
d823b11b
MV
1175unsigned long
1176scm_thread_usleep (unsigned long usecs)
5f05c406 1177{
d823b11b
MV
1178 struct timeval tv;
1179 tv.tv_usec = usecs % 1000000;
1180 tv.tv_sec = usecs / 1000000;
1181 scm_internal_select (0, NULL, NULL, NULL, &tv);
1182 return tv.tv_usec + tv.tv_sec*1000000;
5f05c406
MV
1183}
1184
d823b11b
MV
1185unsigned long
1186scm_thread_sleep (unsigned long secs)
6c214b62 1187{
d823b11b
MV
1188 struct timeval tv;
1189 tv.tv_usec = 0;
1190 tv.tv_sec = secs;
1191 scm_internal_select (0, NULL, NULL, NULL, &tv);
1192 return tv.tv_sec;
6c214b62
MD
1193}
1194
d823b11b
MV
1195/*** Misc */
1196
1197SCM_DEFINE (scm_current_thread, "current-thread", 0, 0, 0,
1198 (void),
1199 "Return the thread that called this function.")
1200#define FUNC_NAME s_scm_current_thread
1201{
1202 return cur_thread;
1203}
1204#undef FUNC_NAME
1205
1206SCM_DEFINE (scm_all_threads, "all-threads", 0, 0, 0,
1207 (void),
1208 "Return a list of all threads.")
9bc4701c 1209#define FUNC_NAME s_scm_all_threads
d823b11b 1210{
0b6843b1 1211 return scm_list_copy (all_threads);
d823b11b
MV
1212}
1213#undef FUNC_NAME
1214
1215scm_root_state *
1216scm_i_thread_root (SCM thread)
1217{
9bc4701c 1218 return ((scm_thread *) SCM_THREAD_DATA (thread))->root;
d823b11b
MV
1219}
1220
1221SCM_DEFINE (scm_thread_exited_p, "thread-exited?", 1, 0, 0,
1222 (SCM thread),
1223 "Return @code{#t} iff @var{thread} has exited.\n")
1224#define FUNC_NAME s_scm_thread_exited_p
1225{
1226 return SCM_BOOL (scm_c_thread_exited_p (thread));
1227}
1228#undef FUNC_NAME
1229
911782b7 1230int
d823b11b
MV
1231scm_c_thread_exited_p (SCM thread)
1232#define FUNC_NAME s_scm_thread_exited_p
5f05c406 1233{
d823b11b
MV
1234 scm_thread *t;
1235 SCM_VALIDATE_THREAD (1, thread);
1236 t = SCM_THREAD_DATA (thread);
1237 return t->exited;
5f05c406 1238}
d823b11b 1239#undef FUNC_NAME
5f05c406 1240
9bc4701c
MD
1241static scm_t_cond wake_up_cond;
1242int scm_i_thread_go_to_sleep;
28d52ebb 1243static scm_t_rec_mutex gc_section_mutex;
9bc4701c
MD
1244static int gc_section_count = 0;
1245static int threads_initialized_p = 0;
1246
1247void
1248scm_i_thread_put_to_sleep ()
1249{
28d52ebb
MD
1250 scm_rec_mutex_lock (&gc_section_mutex);
1251 if (threads_initialized_p && !gc_section_count++)
9bc4701c 1252 {
c4c52ebe
MD
1253 SCM threads;
1254 scm_i_plugin_mutex_lock (&thread_admin_mutex);
1255 threads = all_threads;
9bc4701c
MD
1256 /* Signal all threads to go to sleep */
1257 scm_i_thread_go_to_sleep = 1;
1258 for (; !SCM_NULLP (threads); threads = SCM_CDR (threads))
1259 if (SCM_CAR (threads) != cur_thread)
1260 {
1261 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (threads));
9bc4701c
MD
1262 scm_i_plugin_mutex_lock (&t->heap_mutex);
1263 }
9bc4701c
MD
1264 scm_i_thread_go_to_sleep = 0;
1265 }
1266}
1267
b0dc3d71
MD
1268void
1269scm_i_thread_invalidate_freelists ()
1270{
e29e0b09 1271 /* Don't need to lock thread_admin_mutex here since we are single threaded */
b0dc3d71
MD
1272 SCM threads = all_threads;
1273 for (; !SCM_NULLP (threads); threads = SCM_CDR (threads))
1274 if (SCM_CAR (threads) != cur_thread)
1275 {
1276 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (threads));
1277 t->clear_freelists_p = 1;
1278 }
1279}
1280
9bc4701c
MD
1281void
1282scm_i_thread_wake_up ()
1283{
28d52ebb 1284 if (threads_initialized_p && !--gc_section_count)
9bc4701c 1285 {
c4c52ebe 1286 SCM threads;
c4c52ebe 1287 threads = all_threads;
9bc4701c
MD
1288 scm_i_plugin_cond_broadcast (&wake_up_cond);
1289 for (; !SCM_NULLP (threads); threads = SCM_CDR (threads))
1290 if (SCM_CAR (threads) != cur_thread)
1291 {
1292 scm_thread *t = SCM_THREAD_DATA (SCM_CAR (threads));
1293 scm_i_plugin_mutex_unlock (&t->heap_mutex);
1294 }
c4c52ebe 1295 scm_i_plugin_mutex_unlock (&thread_admin_mutex);
9bc4701c 1296 }
28d52ebb 1297 scm_rec_mutex_unlock (&gc_section_mutex);
9bc4701c
MD
1298}
1299
1300void
1301scm_i_thread_sleep_for_gc ()
1302{
1303 scm_thread *t;
1304 t = suspend ();
9bc4701c 1305 scm_i_plugin_cond_wait (&wake_up_cond, &t->heap_mutex);
b0dc3d71 1306 resume (t);
9bc4701c
MD
1307}
1308
9bc4701c 1309scm_t_mutex scm_i_critical_section_mutex;
28d52ebb
MD
1310scm_t_rec_mutex scm_i_defer_mutex;
1311
1312#ifdef USE_PTHREAD_THREADS
1313#include "libguile/pthread-threads.c"
1314#endif
9bc4701c 1315
d823b11b 1316/*** Initialization */
7bfd3b9e 1317
9bc4701c
MD
1318void
1319scm_threads_prehistory ()
1320{
1321 scm_thread *t;
93cd4dcd
MD
1322#ifdef USE_PTHREAD_THREADS
1323 /* Must be called before any initialization of a mutex. */
1324 scm_init_pthread_threads ();
1325#endif
28d52ebb
MD
1326 scm_i_plugin_mutex_init (&thread_admin_mutex, &scm_i_plugin_mutex);
1327 scm_i_plugin_rec_mutex_init (&gc_section_mutex, &scm_i_plugin_rec_mutex);
9bc4701c 1328 scm_i_plugin_cond_init (&wake_up_cond, 0);
28d52ebb 1329 scm_i_plugin_mutex_init (&scm_i_critical_section_mutex, &scm_i_plugin_mutex);
9bc4701c
MD
1330 thread_count = 1;
1331 scm_i_plugin_key_create (&scm_i_thread_key, 0);
1332 scm_i_plugin_key_create (&scm_i_root_state_key, 0);
28d52ebb 1333 scm_i_plugin_rec_mutex_init (&scm_i_defer_mutex, &scm_i_plugin_rec_mutex);
9bc4701c
MD
1334 /* Allocate a fake thread object to be used during bootup. */
1335 t = malloc (sizeof (scm_thread));
1336 t->base = NULL;
1337 t->clear_freelists_p = 0;
0b6843b1 1338 scm_i_plugin_mutex_init (&t->heap_mutex, &scm_i_plugin_mutex);
9bc4701c 1339 scm_setspecific (scm_i_thread_key, t);
0b6843b1 1340 scm_i_enter_guile (t);
9bc4701c
MD
1341}
1342
d823b11b 1343scm_t_bits scm_tc16_thread;
28d52ebb 1344scm_t_bits scm_tc16_future;
d823b11b 1345scm_t_bits scm_tc16_mutex;
9bc4701c 1346scm_t_bits scm_tc16_fair_mutex;
d823b11b 1347scm_t_bits scm_tc16_condvar;
9bc4701c 1348scm_t_bits scm_tc16_fair_condvar;
7bfd3b9e 1349
7bfd3b9e 1350void
d823b11b 1351scm_init_threads (SCM_STACKITEM *base)
7bfd3b9e 1352{
9bc4701c 1353 SCM thread;
d823b11b 1354 scm_tc16_thread = scm_make_smob_type ("thread", sizeof (scm_thread));
9bc4701c
MD
1355 scm_tc16_mutex = scm_make_smob_type ("mutex", sizeof (scm_t_mutex));
1356 scm_tc16_fair_mutex = scm_make_smob_type ("fair-mutex",
1357 sizeof (fair_mutex));
d823b11b 1358 scm_tc16_condvar = scm_make_smob_type ("condition-variable",
9bc4701c
MD
1359 sizeof (scm_t_cond));
1360 scm_tc16_fair_condvar = scm_make_smob_type ("fair-condition-variable",
1361 sizeof (fair_cond));
d823b11b 1362
9bc4701c
MD
1363 thread = make_thread (SCM_BOOL_F);
1364 /* Replace initial fake thread with a real thread object */
1365 free (SCM_CURRENT_THREAD);
1366 scm_setspecific (scm_i_thread_key, SCM_THREAD_DATA (thread));
1367 scm_i_enter_guile (SCM_CURRENT_THREAD);
d823b11b 1368
d823b11b 1369 /* root is set later from init.c */
9bc4701c 1370 init_thread_creatant (thread, base);
d823b11b
MV
1371 thread_count = 1;
1372 scm_gc_register_root (&all_threads);
9bc4701c 1373 all_threads = scm_cons (thread, SCM_EOL);
d823b11b
MV
1374
1375 scm_set_smob_mark (scm_tc16_thread, thread_mark);
1376 scm_set_smob_print (scm_tc16_thread, thread_print);
1377 scm_set_smob_free (scm_tc16_thread, thread_free);
1378
9bc4701c
MD
1379 scm_set_smob_mark (scm_tc16_fair_mutex, fair_mutex_mark);
1380
1381 scm_set_smob_mark (scm_tc16_fair_condvar, fair_cond_mark);
d823b11b 1382
28d52ebb
MD
1383 scm_tc16_future = scm_make_smob_type ("future", 0);
1384 scm_set_smob_mark (scm_tc16_future, scm_markcdr);
1385 scm_set_smob_free (scm_tc16_future, future_free);
1386 scm_set_smob_print (scm_tc16_future, future_print);
1387
9bc4701c 1388 threads_initialized_p = 1;
7bfd3b9e 1389}
89e00824 1390
5f05c406
MV
1391void
1392scm_init_thread_procs ()
1393{
28d52ebb
MD
1394 scm_loc_sys_thread_handler
1395 = SCM_VARIABLE_LOC (scm_c_define ("%thread-handler", SCM_BOOL_F));
5f05c406
MV
1396#include "libguile/threads.x"
1397}
1398
d823b11b
MV
1399/* XXX */
1400
1401void
1402scm_init_iselect ()
1403{
1404}
1405
89e00824
ML
1406/*
1407 Local Variables:
1408 c-file-style: "gnu"
1409 End:
1410*/