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