Add br-if-logtest opcode
[bpt/guile.git] / libguile / threads.c
CommitLineData
b3da54d1 1/* Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004,
bc8e6d7d
MW
2 * 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
3 * 2014 Free Software Foundation, Inc.
74926120 4 *
73be1d9e 5 * This library is free software; you can redistribute it and/or
53befeb7
NJ
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 3 of
8 * the License, or (at your option) any later version.
7bfd3b9e 9 *
53befeb7
NJ
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
7bfd3b9e 14 *
73be1d9e
MV
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
53befeb7
NJ
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA
73be1d9e 19 */
1bbd0b84 20
1bbd0b84 21
7bfd3b9e 22\f
dbb605f5
LC
23#ifdef HAVE_CONFIG_H
24# include <config.h>
25#endif
7bfd3b9e 26
1c44468d 27#include "libguile/bdw-gc.h"
7af0c3b3 28#include <gc/gc_mark.h>
1810dc4e
RB
29#include "libguile/_scm.h"
30
b3da54d1 31#include <stdlib.h>
d823b11b
MV
32#include <unistd.h>
33#include <stdio.h>
8ab3d8a0
KR
34
35#ifdef HAVE_STRING_H
36#include <string.h> /* for memset used by FD_ZERO on Solaris 10 */
37#endif
38
fcc5d734 39#if HAVE_SYS_TIME_H
d823b11b 40#include <sys/time.h>
fcc5d734 41#endif
5f05c406 42
c1d5d6d7
AW
43#if HAVE_PTHREAD_NP_H
44# include <pthread_np.h>
45#endif
46
6ab4de61
AW
47#include <sys/select.h>
48
f57fdf07 49#include <assert.h>
32f94bf2 50#include <fcntl.h>
d20912e6 51#include <nproc.h>
f57fdf07 52
d823b11b
MV
53#include "libguile/validate.h"
54#include "libguile/root.h"
55#include "libguile/eval.h"
56#include "libguile/async.h"
57#include "libguile/ports.h"
58#include "libguile/threads.h"
a0599745 59#include "libguile/dynwind.h"
d823b11b 60#include "libguile/iselect.h"
9de87eea
MV
61#include "libguile/fluids.h"
62#include "libguile/continuations.h"
2b829bbb 63#include "libguile/gc.h"
aef1fcf9 64#include "libguile/gc-inline.h"
9de87eea 65#include "libguile/init.h"
2e77f720 66#include "libguile/scmsigs.h"
6180e336 67#include "libguile/strings.h"
5f18bc84 68#include "libguile/vm.h"
7bfd3b9e 69
634aa8de
LC
70#include <full-read.h>
71
cde24ce1
AW
72
73\f
7af0c3b3
AW
74
75/* The GC "kind" for threads that allow them to mark their VM
76 stacks. */
77static int thread_gc_kind;
78
79static struct GC_ms_entry *
80thread_mark (GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
81 struct GC_ms_entry *mark_stack_limit, GC_word env)
82{
83 int word;
84 const struct scm_i_thread *t = (struct scm_i_thread *) addr;
85
86 if (SCM_UNPACK (t->handle) == 0)
87 /* T must be on the free-list; ignore. (See warning in
88 gc_mark.h.) */
89 return mark_stack_ptr;
90
5f18bc84 91 /* Mark T. We could be more precise, but it doesn't matter. */
7af0c3b3
AW
92 for (word = 0; word * sizeof (*addr) < sizeof (*t); word++)
93 mark_stack_ptr = GC_MARK_AND_PUSH ((void *) addr[word],
94 mark_stack_ptr, mark_stack_limit,
95 NULL);
96
aef1fcf9
AW
97 /* The pointerless freelists are threaded through their first word,
98 but GC doesn't know to trace them (as they are pointerless), so we
99 need to do that here. See the comments at the top of libgc's
100 gc_inline.h. */
c65ea594
AW
101 if (t->pointerless_freelists)
102 {
103 size_t n;
104 for (n = 0; n < SCM_INLINE_GC_FREELIST_COUNT; n++)
105 {
106 void *chain = t->pointerless_freelists[n];
107 if (chain)
108 {
109 /* The first link is already marked by the freelist vector,
110 so we just have to mark the tail. */
111 while ((chain = *(void **)chain))
112 mark_stack_ptr = GC_mark_and_push (chain, mark_stack_ptr,
113 mark_stack_limit, NULL);
114 }
115 }
116 }
aef1fcf9 117
5f18bc84
AW
118 if (t->vp)
119 mark_stack_ptr = scm_i_vm_mark_stack (t->vp, mark_stack_ptr,
120 mark_stack_limit);
121
7af0c3b3
AW
122 return mark_stack_ptr;
123}
124
125
126\f
6180e336
NJ
127static void
128to_timespec (SCM t, scm_t_timespec *waittime)
129{
130 if (scm_is_pair (t))
131 {
132 waittime->tv_sec = scm_to_ulong (SCM_CAR (t));
133 waittime->tv_nsec = scm_to_ulong (SCM_CDR (t)) * 1000;
134 }
135 else
136 {
137 double time = scm_to_double (t);
138 double sec = scm_c_truncate (time);
139
140 waittime->tv_sec = (long) sec;
2a1d0688 141 waittime->tv_nsec = (long) ((time - sec) * 1000000000);
6180e336
NJ
142 }
143}
144
7af0c3b3 145
c058db8a 146\f
d823b11b 147/*** Queues */
7bfd3b9e 148
c058db8a
LC
149/* Note: We annotate with "GC-robust" assignments whose purpose is to avoid
150 the risk of false references leading to unbounded retained space as
151 described in "Bounding Space Usage of Conservative Garbage Collectors",
152 H.J. Boehm, 2001. */
153
9de87eea
MV
154/* Make an empty queue data structure.
155 */
d823b11b
MV
156static SCM
157make_queue ()
158{
159 return scm_cons (SCM_EOL, SCM_EOL);
160}
7bfd3b9e 161
9de87eea
MV
162/* Put T at the back of Q and return a handle that can be used with
163 remqueue to remove T from Q again.
164 */
d823b11b
MV
165static SCM
166enqueue (SCM q, SCM t)
167{
168 SCM c = scm_cons (t, SCM_EOL);
d2a51087 169 SCM_CRITICAL_SECTION_START;
d2e53ed6 170 if (scm_is_null (SCM_CDR (q)))
d823b11b
MV
171 SCM_SETCDR (q, c);
172 else
173 SCM_SETCDR (SCM_CAR (q), c);
174 SCM_SETCAR (q, c);
d2a51087 175 SCM_CRITICAL_SECTION_END;
d823b11b
MV
176 return c;
177}
7bfd3b9e 178
9de87eea
MV
179/* Remove the element that the handle C refers to from the queue Q. C
180 must have been returned from a call to enqueue. The return value
181 is zero when the element referred to by C has already been removed.
182 Otherwise, 1 is returned.
183*/
184static int
d823b11b
MV
185remqueue (SCM q, SCM c)
186{
187 SCM p, prev = q;
d2a51087 188 SCM_CRITICAL_SECTION_START;
d2e53ed6 189 for (p = SCM_CDR (q); !scm_is_null (p); p = SCM_CDR (p))
d823b11b 190 {
bc36d050 191 if (scm_is_eq (p, c))
d823b11b 192 {
bc36d050 193 if (scm_is_eq (c, SCM_CAR (q)))
10454601 194 SCM_SETCAR (q, scm_is_eq (prev, q) ? SCM_EOL : prev);
d823b11b 195 SCM_SETCDR (prev, SCM_CDR (c));
c058db8a
LC
196
197 /* GC-robust */
198 SCM_SETCDR (c, SCM_EOL);
199
d2a51087 200 SCM_CRITICAL_SECTION_END;
9de87eea 201 return 1;
d823b11b
MV
202 }
203 prev = p;
204 }
d2a51087 205 SCM_CRITICAL_SECTION_END;
9de87eea 206 return 0;
d823b11b
MV
207}
208
9de87eea
MV
209/* Remove the front-most element from the queue Q and return it.
210 Return SCM_BOOL_F when Q is empty.
211*/
d823b11b
MV
212static SCM
213dequeue (SCM q)
214{
d2a51087
NJ
215 SCM c;
216 SCM_CRITICAL_SECTION_START;
217 c = SCM_CDR (q);
d2e53ed6 218 if (scm_is_null (c))
d2a51087
NJ
219 {
220 SCM_CRITICAL_SECTION_END;
221 return SCM_BOOL_F;
222 }
d823b11b
MV
223 else
224 {
225 SCM_SETCDR (q, SCM_CDR (c));
d2e53ed6 226 if (scm_is_null (SCM_CDR (q)))
d823b11b 227 SCM_SETCAR (q, SCM_EOL);
d2a51087 228 SCM_CRITICAL_SECTION_END;
c058db8a
LC
229
230 /* GC-robust */
231 SCM_SETCDR (c, SCM_EOL);
232
d823b11b
MV
233 return SCM_CAR (c);
234 }
235}
7bfd3b9e 236
9de87eea 237/*** Thread smob routines */
76da80e7 238
d823b11b
MV
239
240static int
241thread_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
242{
23d72566
KR
243 /* On a Gnu system pthread_t is an unsigned long, but on mingw it's a
244 struct. A cast like "(unsigned long) t->pthread" is a syntax error in
245 the struct case, hence we go via a union, and extract according to the
246 size of pthread_t. */
247 union {
248 scm_i_pthread_t p;
249 unsigned short us;
250 unsigned int ui;
251 unsigned long ul;
252 scm_t_uintmax um;
253 } u;
9de87eea 254 scm_i_thread *t = SCM_I_THREAD_DATA (exp);
23d72566
KR
255 scm_i_pthread_t p = t->pthread;
256 scm_t_uintmax id;
257 u.p = p;
258 if (sizeof (p) == sizeof (unsigned short))
259 id = u.us;
260 else if (sizeof (p) == sizeof (unsigned int))
261 id = u.ui;
262 else if (sizeof (p) == sizeof (unsigned long))
263 id = u.ul;
264 else
265 id = u.um;
266
0607ebbf 267 scm_puts_unlocked ("#<thread ", port);
23d72566 268 scm_uintprint (id, 10, port);
0607ebbf 269 scm_puts_unlocked (" (", port);
0345e278 270 scm_uintprint ((scm_t_bits)t, 16, port);
0607ebbf 271 scm_puts_unlocked (")>", port);
d823b11b
MV
272 return 1;
273}
274
706846f6 275\f
9de87eea 276/*** Blocking on queues. */
f7eca35d 277
9de87eea
MV
278/* See also scm_i_queue_async_cell for how such a block is
279 interrputed.
280*/
d823b11b 281
9de87eea
MV
282/* Put the current thread on QUEUE and go to sleep, waiting for it to
283 be woken up by a call to 'unblock_from_queue', or to be
284 interrupted. Upon return of this function, the current thread is
285 no longer on QUEUE, even when the sleep has been interrupted.
286
d2a51087 287 The caller of block_self must hold MUTEX. It will be atomically
9de87eea
MV
288 unlocked while sleeping, just as with scm_i_pthread_cond_wait.
289
290 SLEEP_OBJECT is an arbitrary SCM value that is kept alive as long
291 as MUTEX is needed.
292
293 When WAITTIME is not NULL, the sleep will be aborted at that time.
294
295 The return value of block_self is an errno value. It will be zero
296 when the sleep has been successfully completed by a call to
297 unblock_from_queue, EINTR when it has been interrupted by the
298 delivery of a system async, and ETIMEDOUT when the timeout has
299 expired.
300
301 The system asyncs themselves are not executed by block_self.
302*/
303static int
304block_self (SCM queue, SCM sleep_object, scm_i_pthread_mutex_t *mutex,
305 const scm_t_timespec *waittime)
76da80e7 306{
9de87eea
MV
307 scm_i_thread *t = SCM_I_CURRENT_THREAD;
308 SCM q_handle;
309 int err;
310
311 if (scm_i_setup_sleep (t, sleep_object, mutex, -1))
312 err = EINTR;
313 else
314 {
315 t->block_asyncs++;
316 q_handle = enqueue (queue, t->handle);
317 if (waittime == NULL)
318 err = scm_i_scm_pthread_cond_wait (&t->sleep_cond, mutex);
319 else
320 err = scm_i_scm_pthread_cond_timedwait (&t->sleep_cond, mutex, waittime);
321
322 /* When we are still on QUEUE, we have been interrupted. We
323 report this only when no other error (such as a timeout) has
324 happened above.
325 */
326 if (remqueue (queue, q_handle) && err == 0)
327 err = EINTR;
328 t->block_asyncs--;
329 scm_i_reset_sleep (t);
330 }
331
332 return err;
76da80e7 333}
9de87eea 334
d2a51087
NJ
335/* Wake up the first thread on QUEUE, if any. The awoken thread is
336 returned, or #f if the queue was empty.
9de87eea
MV
337 */
338static SCM
339unblock_from_queue (SCM queue)
340{
341 SCM thread = dequeue (queue);
342 if (scm_is_true (thread))
343 scm_i_pthread_cond_signal (&SCM_I_THREAD_DATA(thread)->sleep_cond);
344 return thread;
345}
346
45f15cac 347\f
9de87eea
MV
348/* Getting into and out of guile mode.
349 */
350
f60a7648
AW
351/* Key used to attach a cleanup handler to a given thread. Also, if
352 thread-local storage is unavailable, this key is used to retrieve the
353 current thread with `pthread_getspecific ()'. */
354scm_i_pthread_key_t scm_i_thread_key;
355
356
705edb95
LC
357#ifdef SCM_HAVE_THREAD_STORAGE_CLASS
358
359/* When thread-local storage (TLS) is available, a pointer to the
360 current-thread object is kept in TLS. Note that storing the thread-object
361 itself in TLS (rather than a pointer to some malloc'd memory) is not
362 possible since thread objects may live longer than the actual thread they
363 represent. */
364SCM_THREAD_LOCAL scm_i_thread *scm_i_current_thread = NULL;
365
f60a7648 366#endif /* SCM_HAVE_THREAD_STORAGE_CLASS */
705edb95 367
d823b11b 368
9de87eea
MV
369static scm_i_pthread_mutex_t thread_admin_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
370static scm_i_thread *all_threads = NULL;
371static int thread_count;
372
373static SCM scm_i_default_dynamic_state;
374
0b77014f
AW
375/* Run when a fluid is collected. */
376void
aafb4ed7 377scm_i_reset_fluid (size_t n)
0b77014f
AW
378{
379 scm_i_thread *t;
380
381 scm_i_pthread_mutex_lock (&thread_admin_mutex);
382 for (t = all_threads; t; t = t->next_thread)
383 if (SCM_I_DYNAMIC_STATE_P (t->dynamic_state))
384 {
385 SCM v = SCM_I_DYNAMIC_STATE_FLUIDS (t->dynamic_state);
386
387 if (n < SCM_SIMPLE_VECTOR_LENGTH (v))
aafb4ed7 388 SCM_SIMPLE_VECTOR_SET (v, n, SCM_UNDEFINED);
0b77014f
AW
389 }
390 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
391}
392
9de87eea 393/* Perform first stage of thread initialisation, in non-guile mode.
d823b11b 394 */
9de87eea 395static void
12c1d861 396guilify_self_1 (struct GC_stack_base *base)
d823b11b 397{
7f22442b
AW
398 scm_i_thread t;
399
400 /* We must arrange for SCM_I_CURRENT_THREAD to point to a valid value
401 before allocating anything in this thread, because allocation could
402 cause GC to run, and GC could cause finalizers, which could invoke
403 Scheme functions, which need the current thread to be set. */
404
405 t.pthread = scm_i_pthread_self ();
406 t.handle = SCM_BOOL_F;
407 t.result = SCM_BOOL_F;
408 t.cleanup_handler = SCM_BOOL_F;
409 t.mutexes = SCM_EOL;
410 t.held_mutex = NULL;
411 t.join_queue = SCM_EOL;
aef1fcf9
AW
412 t.freelists = NULL;
413 t.pointerless_freelists = NULL;
7f22442b 414 t.dynamic_state = SCM_BOOL_F;
9ede013f
AW
415 t.dynstack.base = NULL;
416 t.dynstack.top = NULL;
417 t.dynstack.limit = NULL;
7f22442b
AW
418 t.active_asyncs = SCM_EOL;
419 t.block_asyncs = 1;
420 t.pending_asyncs = 1;
421 t.critical_section_level = 0;
422 t.base = base->mem_base;
346e4402 423#ifdef __ia64__
bbd21051 424 t.register_backing_store_base = base->reg_base;
346e4402 425#endif
7f22442b
AW
426 t.continuation_root = SCM_EOL;
427 t.continuation_base = t.base;
428 scm_i_pthread_cond_init (&t.sleep_cond, NULL);
429 t.sleep_mutex = NULL;
430 t.sleep_object = SCM_BOOL_F;
431 t.sleep_fd = -1;
7af0c3b3 432 t.vp = NULL;
7f22442b 433
32f94bf2 434 if (pipe2 (t.sleep_pipe, O_CLOEXEC) != 0)
634aa8de
LC
435 /* FIXME: Error conditions during the initialization phase are handled
436 gracelessly since public functions such as `scm_init_guile ()'
437 currently have type `void'. */
438 abort ();
439
7f22442b 440 scm_i_pthread_mutex_init (&t.admin_mutex, NULL);
7f22442b
AW
441 t.canceled = 0;
442 t.exited = 0;
443 t.guile_mode = 0;
9de87eea 444
7f22442b
AW
445 /* The switcheroo. */
446 {
447 scm_i_thread *t_ptr = &t;
448
449 GC_disable ();
7af0c3b3 450 t_ptr = GC_generic_malloc (sizeof (*t_ptr), thread_gc_kind);
7f22442b
AW
451 memcpy (t_ptr, &t, sizeof t);
452
453 scm_i_pthread_setspecific (scm_i_thread_key, t_ptr);
f60a7648
AW
454
455#ifdef SCM_HAVE_THREAD_STORAGE_CLASS
7f22442b
AW
456 /* Cache the current thread in TLS for faster lookup. */
457 scm_i_current_thread = t_ptr;
f60a7648 458#endif
9de87eea 459
7f22442b
AW
460 scm_i_pthread_mutex_lock (&thread_admin_mutex);
461 t_ptr->next_thread = all_threads;
462 all_threads = t_ptr;
463 thread_count++;
464 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
465
466 GC_enable ();
467 }
d823b11b
MV
468}
469
9de87eea 470/* Perform second stage of thread initialisation, in guile mode.
d823b11b 471 */
9de87eea
MV
472static void
473guilify_self_2 (SCM parent)
d823b11b 474{
9de87eea
MV
475 scm_i_thread *t = SCM_I_CURRENT_THREAD;
476
72e6b608
LC
477 t->guile_mode = 1;
478
9de87eea 479 SCM_NEWSMOB (t->handle, scm_tc16_thread, t);
c812243b 480
9de87eea
MV
481 t->continuation_root = scm_cons (t->handle, SCM_EOL);
482 t->continuation_base = t->base;
483
aef1fcf9
AW
484 {
485 size_t size = SCM_INLINE_GC_FREELIST_COUNT * sizeof (void *);
486 t->freelists = scm_gc_malloc (size, "freelists");
487 t->pointerless_freelists = scm_gc_malloc (size, "atomic freelists");
488 }
489
9de87eea
MV
490 if (scm_is_true (parent))
491 t->dynamic_state = scm_make_dynamic_state (parent);
492 else
493 t->dynamic_state = scm_i_make_initial_dynamic_state ();
494
9ede013f
AW
495 t->dynstack.base = scm_gc_malloc (16 * sizeof (scm_t_bits), "dynstack");
496 t->dynstack.limit = t->dynstack.base + 16;
497 t->dynstack.top = t->dynstack.base + SCM_DYNSTACK_HEADER_LEN;
498
9de87eea
MV
499 t->join_queue = make_queue ();
500 t->block_asyncs = 0;
f740445a
AW
501
502 /* See note in finalizers.c:queue_finalizer_async(). */
503 GC_invoke_finalizers ();
d823b11b
MV
504}
505
6180e336
NJ
506\f
507/*** Fat mutexes */
508
509/* We implement our own mutex type since we want them to be 'fair', we
510 want to do fancy things while waiting for them (like running
511 asyncs) and we might want to add things that are nice for
512 debugging.
513*/
514
515typedef struct {
516 scm_i_pthread_mutex_t lock;
517 SCM owner;
adc085f1 518 int level; /* how much the owner owns us. <= 1 for non-recursive mutexes */
6180e336 519
adc085f1 520 int recursive; /* allow recursive locking? */
6180e336
NJ
521 int unchecked_unlock; /* is it an error to unlock an unlocked mutex? */
522 int allow_external_unlock; /* is it an error to unlock a mutex that is not
523 owned by the current thread? */
524
525 SCM waiting; /* the threads waiting for this mutex. */
526} fat_mutex;
527
528#define SCM_MUTEXP(x) SCM_SMOB_PREDICATE (scm_tc16_mutex, x)
529#define SCM_MUTEX_DATA(x) ((fat_mutex *) SCM_SMOB_DATA (x))
530
2002f1f8
AW
531static SCM
532call_cleanup (void *data)
533{
534 SCM *proc_p = data;
535 return scm_call_0 (*proc_p);
536}
537
9de87eea 538/* Perform thread tear-down, in guile mode.
d823b11b 539 */
9de87eea
MV
540static void *
541do_thread_exit (void *v)
542{
2e77f720
LC
543 scm_i_thread *t = (scm_i_thread *) v;
544
545 if (!scm_is_false (t->cleanup_handler))
546 {
547 SCM ptr = t->cleanup_handler;
548
549 t->cleanup_handler = SCM_BOOL_F;
550 t->result = scm_internal_catch (SCM_BOOL_T,
2002f1f8 551 call_cleanup, &ptr,
2e77f720
LC
552 scm_handle_by_message_noexit, NULL);
553 }
9de87eea 554
86a597f8 555 scm_i_scm_pthread_mutex_lock (&t->admin_mutex);
9de87eea
MV
556
557 t->exited = 1;
0c97d7dd
MV
558 close (t->sleep_pipe[0]);
559 close (t->sleep_pipe[1]);
9de87eea
MV
560 while (scm_is_true (unblock_from_queue (t->join_queue)))
561 ;
9de87eea 562
74926120 563 while (!scm_is_null (t->mutexes))
6180e336 564 {
a141db86 565 SCM mutex = scm_c_weak_vector_ref (scm_car (t->mutexes), 0);
74926120 566
a141db86 567 if (scm_is_true (mutex))
a0faf7dd
LC
568 {
569 fat_mutex *m = SCM_MUTEX_DATA (mutex);
570
571 scm_i_pthread_mutex_lock (&m->lock);
f57fdf07 572
02a362a6
LC
573 /* Check whether T owns MUTEX. This is usually the case, unless
574 T abandoned MUTEX; in that case, T is no longer its owner (see
575 `fat_mutex_lock') but MUTEX is still in `t->mutexes'. */
576 if (scm_is_eq (m->owner, t->handle))
577 unblock_from_queue (m->waiting);
f57fdf07 578
a0faf7dd
LC
579 scm_i_pthread_mutex_unlock (&m->lock);
580 }
6180e336 581
a141db86 582 t->mutexes = scm_cdr (t->mutexes);
6180e336
NJ
583 }
584
86a597f8 585 scm_i_pthread_mutex_unlock (&t->admin_mutex);
2e77f720 586
9de87eea
MV
587 return NULL;
588}
589
f60a7648
AW
590static void *
591do_thread_exit_trampoline (struct GC_stack_base *sb, void *v)
592{
7f22442b 593 /* Won't hurt if we are already registered. */
8e76ce94 594#if SCM_USE_PTHREAD_THREADS
7f22442b 595 GC_register_my_thread (sb);
8e76ce94 596#endif
f60a7648 597
7f22442b 598 return scm_with_guile (do_thread_exit, v);
f60a7648
AW
599}
600
d823b11b 601static void
9de87eea 602on_thread_exit (void *v)
d823b11b 603{
29776e85 604 /* This handler is executed in non-guile mode. */
2e77f720 605 scm_i_thread *t = (scm_i_thread *) v, **tp;
0c97d7dd 606
b8441577
LC
607 /* If we were canceled, we were unable to clear `t->guile_mode', so do
608 it here. */
609 t->guile_mode = 0;
610
d2a51087
NJ
611 /* If this thread was cancelled while doing a cond wait, it will
612 still have a mutex locked, so we unlock it here. */
613 if (t->held_mutex)
614 {
615 scm_i_pthread_mutex_unlock (t->held_mutex);
616 t->held_mutex = NULL;
617 }
618
f60a7648
AW
619 /* Reinstate the current thread for purposes of scm_with_guile
620 guile-mode cleanup handlers. Only really needed in the non-TLS
621 case but it doesn't hurt to be consistent. */
622 scm_i_pthread_setspecific (scm_i_thread_key, t);
0c97d7dd 623
7f22442b
AW
624 /* Scheme-level thread finalizers and other cleanup needs to happen in
625 guile mode. */
626 GC_call_with_stack_base (do_thread_exit_trampoline, t);
0c97d7dd
MV
627
628 /* Removing ourself from the list of all threads needs to happen in
629 non-guile mode since all SCM values on our stack become
29776e85 630 unprotected once we are no longer in the list. */
0c97d7dd
MV
631 scm_i_pthread_mutex_lock (&thread_admin_mutex);
632 for (tp = &all_threads; *tp; tp = &(*tp)->next_thread)
633 if (*tp == t)
634 {
635 *tp = t->next_thread;
c058db8a
LC
636
637 /* GC-robust */
638 t->next_thread = NULL;
639
0c97d7dd
MV
640 break;
641 }
642 thread_count--;
2e77f720
LC
643
644 /* If there's only one other thread, it could be the signal delivery
645 thread, so we need to notify it to shut down by closing its read pipe.
646 If it's not the signal delivery thread, then closing the read pipe isn't
647 going to hurt. */
648 if (thread_count <= 1)
649 scm_i_close_signal_pipe ();
650
0c97d7dd
MV
651 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
652
f60a7648 653 scm_i_pthread_setspecific (scm_i_thread_key, NULL);
7f22442b 654
5f18bc84
AW
655 if (t->vp)
656 {
657 scm_i_vm_free_stack (t->vp);
658 t->vp = NULL;
659 }
660
8e76ce94 661#if SCM_USE_PTHREAD_THREADS
7f22442b 662 GC_unregister_my_thread ();
653ccd78 663#endif
d823b11b
MV
664}
665
9de87eea 666static scm_i_pthread_once_t init_thread_key_once = SCM_I_PTHREAD_ONCE_INIT;
d823b11b 667
9de87eea
MV
668static void
669init_thread_key (void)
670{
f60a7648 671 scm_i_pthread_key_create (&scm_i_thread_key, on_thread_exit);
9de87eea 672}
d823b11b 673
cde24ce1
AW
674/* Perform any initializations necessary to make the current thread
675 known to Guile (via SCM_I_CURRENT_THREAD), initializing Guile itself,
676 if necessary.
a54a94b3 677
9de87eea
MV
678 BASE is the stack base to use with GC.
679
680 PARENT is the dynamic state to use as the parent, ot SCM_BOOL_F in
681 which case the default dynamic state is used.
682
cde24ce1 683 Returns zero when the thread was known to guile already; otherwise
9de87eea 684 return 1.
cde24ce1
AW
685
686 Note that it could be the case that the thread was known
687 to Guile, but not in guile mode (because we are within a
688 scm_without_guile call). Check SCM_I_CURRENT_THREAD->guile_mode to
689 be sure. New threads are put into guile mode implicitly. */
9de87eea
MV
690
691static int
12c1d861 692scm_i_init_thread_for_guile (struct GC_stack_base *base, SCM parent)
d823b11b 693{
9de87eea
MV
694 scm_i_pthread_once (&init_thread_key_once, init_thread_key);
695
cde24ce1
AW
696 if (SCM_I_CURRENT_THREAD)
697 {
698 /* Thread is already known to Guile.
699 */
700 return 0;
701 }
702 else
9de87eea
MV
703 {
704 /* This thread has not been guilified yet.
705 */
706
707 scm_i_pthread_mutex_lock (&scm_i_init_mutex);
708 if (scm_initialized_p == 0)
709 {
710 /* First thread ever to enter Guile. Run the full
711 initialization.
712 */
713 scm_i_init_guile (base);
12c1d861 714
03d12949 715#if SCM_USE_PTHREAD_THREADS
12c1d861
AW
716 /* Allow other threads to come in later. */
717 GC_allow_register_threads ();
4000d064 718#endif
12c1d861 719
9de87eea
MV
720 scm_i_pthread_mutex_unlock (&scm_i_init_mutex);
721 }
722 else
723 {
724 /* Guile is already initialized, but this thread enters it for
725 the first time. Only initialize this thread.
726 */
727 scm_i_pthread_mutex_unlock (&scm_i_init_mutex);
12c1d861
AW
728
729 /* Register this thread with libgc. */
8e76ce94 730#if SCM_USE_PTHREAD_THREADS
12c1d861 731 GC_register_my_thread (base);
8e76ce94 732#endif
12c1d861 733
9de87eea
MV
734 guilify_self_1 (base);
735 guilify_self_2 (parent);
736 }
737 return 1;
738 }
d823b11b
MV
739}
740
12c1d861
AW
741void
742scm_init_guile ()
d823b11b 743{
12c1d861
AW
744 struct GC_stack_base stack_base;
745
746 if (GC_get_stack_base (&stack_base) == GC_SUCCESS)
747 scm_i_init_thread_for_guile (&stack_base,
748 scm_i_default_dynamic_state);
9de87eea
MV
749 else
750 {
12c1d861 751 fprintf (stderr, "Failed to get stack base for current thread.\n");
4765b28f 752 exit (EXIT_FAILURE);
9de87eea 753 }
a54a94b3
MV
754}
755
12c1d861 756struct with_guile_args
cde24ce1
AW
757{
758 GC_fn_type func;
759 void *data;
12c1d861 760 SCM parent;
cde24ce1
AW
761};
762
763static void *
764with_guile_trampoline (void *data)
765{
12c1d861 766 struct with_guile_args *args = data;
cde24ce1
AW
767
768 return scm_c_with_continuation_barrier (args->func, args->data);
769}
770
12c1d861
AW
771static void *
772with_guile_and_parent (struct GC_stack_base *base, void *data)
9de87eea
MV
773{
774 void *res;
cde24ce1
AW
775 int new_thread;
776 scm_i_thread *t;
12c1d861 777 struct with_guile_args *args = data;
2e77f720 778
12c1d861 779 new_thread = scm_i_init_thread_for_guile (base, args->parent);
cde24ce1
AW
780 t = SCM_I_CURRENT_THREAD;
781 if (new_thread)
2e77f720 782 {
cde24ce1
AW
783 /* We are in Guile mode. */
784 assert (t->guile_mode);
785
12c1d861 786 res = scm_c_with_continuation_barrier (args->func, args->data);
cde24ce1
AW
787
788 /* Leave Guile mode. */
789 t->guile_mode = 0;
790 }
791 else if (t->guile_mode)
792 {
793 /* Already in Guile mode. */
12c1d861 794 res = scm_c_with_continuation_barrier (args->func, args->data);
2e77f720 795 }
74926120 796 else
cde24ce1 797 {
cde24ce1
AW
798 /* We are not in Guile mode, either because we are not within a
799 scm_with_guile, or because we are within a scm_without_guile.
72e6b608 800
cde24ce1
AW
801 This call to scm_with_guile() could happen from anywhere on the
802 stack, and in particular lower on the stack than when it was
803 when this thread was first guilified. Thus, `base' must be
804 updated. */
805#if SCM_STACK_GROWS_UP
12c1d861
AW
806 if (SCM_STACK_PTR (base->mem_base) < t->base)
807 t->base = SCM_STACK_PTR (base->mem_base);
cde24ce1 808#else
12c1d861
AW
809 if (SCM_STACK_PTR (base->mem_base) > t->base)
810 t->base = SCM_STACK_PTR (base->mem_base);
cde24ce1 811#endif
72e6b608 812
cde24ce1 813 t->guile_mode = 1;
03d12949 814 res = GC_call_with_gc_active (with_guile_trampoline, args);
cde24ce1
AW
815 t->guile_mode = 0;
816 }
817 return res;
72e6b608 818}
3d1af79f 819
12c1d861
AW
820static void *
821scm_i_with_guile_and_parent (void *(*func)(void *), void *data, SCM parent)
822{
823 struct with_guile_args args;
824
825 args.func = func;
826 args.data = data;
827 args.parent = parent;
828
829 return GC_call_with_stack_base (with_guile_and_parent, &args);
830}
831
832void *
833scm_with_guile (void *(*func)(void *), void *data)
834{
835 return scm_i_with_guile_and_parent (func, data,
836 scm_i_default_dynamic_state);
837}
838
9de87eea
MV
839void *
840scm_without_guile (void *(*func)(void *), void *data)
d823b11b 841{
72e6b608 842 void *result;
cde24ce1 843 scm_i_thread *t = SCM_I_CURRENT_THREAD;
72e6b608 844
cde24ce1 845 if (t->guile_mode)
72e6b608 846 {
cde24ce1 847 SCM_I_CURRENT_THREAD->guile_mode = 0;
03d12949 848 result = GC_do_blocking (func, data);
cde24ce1 849 SCM_I_CURRENT_THREAD->guile_mode = 1;
72e6b608
LC
850 }
851 else
cde24ce1 852 /* Otherwise we're not in guile mode, so nothing to do. */
72e6b608
LC
853 result = func (data);
854
855 return result;
9de87eea
MV
856}
857
72e6b608 858\f
9de87eea
MV
859/*** Thread creation */
860
861typedef struct {
862 SCM parent;
863 SCM thunk;
864 SCM handler;
76da80e7 865 SCM thread;
9de87eea
MV
866 scm_i_pthread_mutex_t mutex;
867 scm_i_pthread_cond_t cond;
868} launch_data;
d823b11b 869
9de87eea
MV
870static void *
871really_launch (void *d)
872{
873 launch_data *data = (launch_data *)d;
874 SCM thunk = data->thunk, handler = data->handler;
875 scm_i_thread *t;
d823b11b 876
9de87eea 877 t = SCM_I_CURRENT_THREAD;
a54a94b3 878
9de87eea
MV
879 scm_i_scm_pthread_mutex_lock (&data->mutex);
880 data->thread = scm_current_thread ();
881 scm_i_pthread_cond_signal (&data->cond);
882 scm_i_pthread_mutex_unlock (&data->mutex);
883
884 if (SCM_UNBNDP (handler))
885 t->result = scm_call_0 (thunk);
886 else
887 t->result = scm_catch (SCM_BOOL_T, thunk, handler);
888
889 return 0;
d823b11b
MV
890}
891
9de87eea
MV
892static void *
893launch_thread (void *d)
894{
895 launch_data *data = (launch_data *)d;
896 scm_i_pthread_detach (scm_i_pthread_self ());
897 scm_i_with_guile_and_parent (really_launch, d, data->parent);
898 return NULL;
899}
900
901SCM_DEFINE (scm_call_with_new_thread, "call-with-new-thread", 1, 1, 0,
d823b11b 902 (SCM thunk, SCM handler),
9de87eea
MV
903 "Call @code{thunk} in a new thread and with a new dynamic state,\n"
904 "returning a new thread object representing the thread. The procedure\n"
905 "@var{thunk} is called via @code{with-continuation-barrier}.\n"
906 "\n"
907 "When @var{handler} is specified, then @var{thunk} is called from\n"
908 "within a @code{catch} with tag @code{#t} that has @var{handler} as its\n"
909 "handler. This catch is established inside the continuation barrier.\n"
910 "\n"
911 "Once @var{thunk} or @var{handler} returns, the return value is made\n"
912 "the @emph{exit value} of the thread and the thread is terminated.")
d823b11b
MV
913#define FUNC_NAME s_scm_call_with_new_thread
914{
9de87eea
MV
915 launch_data data;
916 scm_i_pthread_t id;
917 int err;
d823b11b 918
9de87eea
MV
919 SCM_ASSERT (scm_is_true (scm_thunk_p (thunk)), thunk, SCM_ARG1, FUNC_NAME);
920 SCM_ASSERT (SCM_UNBNDP (handler) || scm_is_true (scm_procedure_p (handler)),
921 handler, SCM_ARG2, FUNC_NAME);
922
9f7537dc 923 GC_collect_a_little ();
9de87eea
MV
924 data.parent = scm_current_dynamic_state ();
925 data.thunk = thunk;
926 data.handler = handler;
927 data.thread = SCM_BOOL_F;
928 scm_i_pthread_mutex_init (&data.mutex, NULL);
929 scm_i_pthread_cond_init (&data.cond, NULL);
930
931 scm_i_scm_pthread_mutex_lock (&data.mutex);
932 err = scm_i_pthread_create (&id, NULL, launch_thread, &data);
933 if (err)
934 {
935 scm_i_pthread_mutex_unlock (&data.mutex);
936 errno = err;
937 scm_syserror (NULL);
938 }
dbab8aaa
AW
939
940 while (scm_is_false (data.thread))
941 scm_i_scm_pthread_cond_wait (&data.cond, &data.mutex);
942
9de87eea 943 scm_i_pthread_mutex_unlock (&data.mutex);
74926120 944
9de87eea 945 return data.thread;
d823b11b
MV
946}
947#undef FUNC_NAME
948
9de87eea
MV
949typedef struct {
950 SCM parent;
951 scm_t_catch_body body;
952 void *body_data;
953 scm_t_catch_handler handler;
954 void *handler_data;
955 SCM thread;
956 scm_i_pthread_mutex_t mutex;
957 scm_i_pthread_cond_t cond;
958} spawn_data;
959
960static void *
961really_spawn (void *d)
962{
963 spawn_data *data = (spawn_data *)d;
964 scm_t_catch_body body = data->body;
965 void *body_data = data->body_data;
966 scm_t_catch_handler handler = data->handler;
967 void *handler_data = data->handler_data;
968 scm_i_thread *t = SCM_I_CURRENT_THREAD;
969
970 scm_i_scm_pthread_mutex_lock (&data->mutex);
971 data->thread = scm_current_thread ();
972 scm_i_pthread_cond_signal (&data->cond);
973 scm_i_pthread_mutex_unlock (&data->mutex);
974
975 if (handler == NULL)
976 t->result = body (body_data);
977 else
978 t->result = scm_internal_catch (SCM_BOOL_T,
979 body, body_data,
980 handler, handler_data);
981
982 return 0;
983}
984
985static void *
986spawn_thread (void *d)
987{
988 spawn_data *data = (spawn_data *)d;
989 scm_i_pthread_detach (scm_i_pthread_self ());
990 scm_i_with_guile_and_parent (really_spawn, d, data->parent);
991 return NULL;
992}
993
994SCM
995scm_spawn_thread (scm_t_catch_body body, void *body_data,
996 scm_t_catch_handler handler, void *handler_data)
997{
998 spawn_data data;
999 scm_i_pthread_t id;
1000 int err;
1001
1002 data.parent = scm_current_dynamic_state ();
1003 data.body = body;
1004 data.body_data = body_data;
1005 data.handler = handler;
1006 data.handler_data = handler_data;
1007 data.thread = SCM_BOOL_F;
1008 scm_i_pthread_mutex_init (&data.mutex, NULL);
1009 scm_i_pthread_cond_init (&data.cond, NULL);
1010
1011 scm_i_scm_pthread_mutex_lock (&data.mutex);
1012 err = scm_i_pthread_create (&id, NULL, spawn_thread, &data);
1013 if (err)
1014 {
1015 scm_i_pthread_mutex_unlock (&data.mutex);
1016 errno = err;
1017 scm_syserror (NULL);
1018 }
dbab8aaa
AW
1019
1020 while (scm_is_false (data.thread))
1021 scm_i_scm_pthread_cond_wait (&data.cond, &data.mutex);
1022
9de87eea 1023 scm_i_pthread_mutex_unlock (&data.mutex);
74926120 1024
0f4f2d9a
LC
1025 assert (SCM_I_IS_THREAD (data.thread));
1026
9de87eea
MV
1027 return data.thread;
1028}
1029
29717c89
MD
1030SCM_DEFINE (scm_yield, "yield", 0, 0, 0,
1031 (),
1032"Move the calling thread to the end of the scheduling queue.")
1033#define FUNC_NAME s_scm_yield
1034{
9de87eea 1035 return scm_from_bool (scm_i_sched_yield ());
29717c89
MD
1036}
1037#undef FUNC_NAME
1038
2e77f720
LC
1039SCM_DEFINE (scm_cancel_thread, "cancel-thread", 1, 0, 0,
1040 (SCM thread),
1041"Asynchronously force the target @var{thread} to terminate. @var{thread} "
1042"cannot be the current thread, and if @var{thread} has already terminated or "
1043"been signaled to terminate, this function is a no-op.")
1044#define FUNC_NAME s_scm_cancel_thread
1045{
1046 scm_i_thread *t = NULL;
1047
1048 SCM_VALIDATE_THREAD (1, thread);
1049 t = SCM_I_THREAD_DATA (thread);
86a597f8 1050 scm_i_scm_pthread_mutex_lock (&t->admin_mutex);
2e77f720
LC
1051 if (!t->canceled)
1052 {
1053 t->canceled = 1;
86a597f8 1054 scm_i_pthread_mutex_unlock (&t->admin_mutex);
2e77f720
LC
1055 scm_i_pthread_cancel (t->pthread);
1056 }
1057 else
86a597f8 1058 scm_i_pthread_mutex_unlock (&t->admin_mutex);
2e77f720
LC
1059
1060 return SCM_UNSPECIFIED;
1061}
1062#undef FUNC_NAME
1063
1064SCM_DEFINE (scm_set_thread_cleanup_x, "set-thread-cleanup!", 2, 0, 0,
1065 (SCM thread, SCM proc),
1066"Set the thunk @var{proc} as the cleanup handler for the thread @var{thread}. "
1067"This handler will be called when the thread exits.")
1068#define FUNC_NAME s_scm_set_thread_cleanup_x
1069{
1070 scm_i_thread *t;
1071
1072 SCM_VALIDATE_THREAD (1, thread);
1073 if (!scm_is_false (proc))
1074 SCM_VALIDATE_THUNK (2, proc);
1075
2e77f720 1076 t = SCM_I_THREAD_DATA (thread);
86a597f8
NJ
1077 scm_i_pthread_mutex_lock (&t->admin_mutex);
1078
2e77f720
LC
1079 if (!(t->exited || t->canceled))
1080 t->cleanup_handler = proc;
1081
86a597f8 1082 scm_i_pthread_mutex_unlock (&t->admin_mutex);
2e77f720
LC
1083
1084 return SCM_UNSPECIFIED;
1085}
1086#undef FUNC_NAME
1087
1088SCM_DEFINE (scm_thread_cleanup, "thread-cleanup", 1, 0, 0,
1089 (SCM thread),
1090"Return the cleanup handler installed for the thread @var{thread}.")
1091#define FUNC_NAME s_scm_thread_cleanup
1092{
1093 scm_i_thread *t;
1094 SCM ret;
1095
1096 SCM_VALIDATE_THREAD (1, thread);
1097
2e77f720 1098 t = SCM_I_THREAD_DATA (thread);
86a597f8 1099 scm_i_pthread_mutex_lock (&t->admin_mutex);
2e77f720 1100 ret = (t->exited || t->canceled) ? SCM_BOOL_F : t->cleanup_handler;
86a597f8 1101 scm_i_pthread_mutex_unlock (&t->admin_mutex);
2e77f720
LC
1102
1103 return ret;
1104}
1105#undef FUNC_NAME
1106
6180e336
NJ
1107SCM scm_join_thread (SCM thread)
1108{
1109 return scm_join_thread_timed (thread, SCM_UNDEFINED, SCM_UNDEFINED);
1110}
1111
1112SCM_DEFINE (scm_join_thread_timed, "join-thread", 1, 2, 0,
1113 (SCM thread, SCM timeout, SCM timeoutval),
d823b11b
MV
1114"Suspend execution of the calling thread until the target @var{thread} "
1115"terminates, unless the target @var{thread} has already terminated. ")
6180e336 1116#define FUNC_NAME s_scm_join_thread_timed
5f05c406 1117{
9de87eea 1118 scm_i_thread *t;
6180e336
NJ
1119 scm_t_timespec ctimeout, *timeout_ptr = NULL;
1120 SCM res = SCM_BOOL_F;
1121
1122 if (! (SCM_UNBNDP (timeoutval)))
1123 res = timeoutval;
d823b11b
MV
1124
1125 SCM_VALIDATE_THREAD (1, thread);
9de87eea 1126 if (scm_is_eq (scm_current_thread (), thread))
2e77f720 1127 SCM_MISC_ERROR ("cannot join the current thread", SCM_EOL);
d823b11b 1128
9de87eea 1129 t = SCM_I_THREAD_DATA (thread);
86a597f8
NJ
1130 scm_i_scm_pthread_mutex_lock (&t->admin_mutex);
1131
6180e336
NJ
1132 if (! SCM_UNBNDP (timeout))
1133 {
1134 to_timespec (timeout, &ctimeout);
1135 timeout_ptr = &ctimeout;
1136 }
1137
1138 if (t->exited)
1139 res = t->result;
1140 else
d823b11b 1141 {
9de87eea
MV
1142 while (1)
1143 {
74926120 1144 int err = block_self (t->join_queue, thread, &t->admin_mutex,
6180e336
NJ
1145 timeout_ptr);
1146 if (err == 0)
1147 {
1148 if (t->exited)
1149 {
1150 res = t->result;
1151 break;
1152 }
1153 }
1154 else if (err == ETIMEDOUT)
9de87eea 1155 break;
6180e336 1156
86a597f8 1157 scm_i_pthread_mutex_unlock (&t->admin_mutex);
9de87eea 1158 SCM_TICK;
86a597f8 1159 scm_i_scm_pthread_mutex_lock (&t->admin_mutex);
21346c4f
NJ
1160
1161 /* Check for exit again, since we just released and
1162 reacquired the admin mutex, before the next block_self
1163 call (which would block forever if t has already
1164 exited). */
1165 if (t->exited)
1166 {
1167 res = t->result;
1168 break;
1169 }
9de87eea 1170 }
d823b11b 1171 }
9de87eea 1172
86a597f8 1173 scm_i_pthread_mutex_unlock (&t->admin_mutex);
2e77f720 1174
d823b11b 1175 return res;
5f05c406
MV
1176}
1177#undef FUNC_NAME
1178
6180e336
NJ
1179SCM_DEFINE (scm_thread_p, "thread?", 1, 0, 0,
1180 (SCM obj),
1181 "Return @code{#t} if @var{obj} is a thread.")
1182#define FUNC_NAME s_scm_thread_p
1183{
1184 return SCM_I_IS_THREAD(obj) ? SCM_BOOL_T : SCM_BOOL_F;
1185}
1186#undef FUNC_NAME
5f05c406 1187
4079f87e 1188
76da80e7 1189static int
9de87eea 1190fat_mutex_print (SCM mx, SCM port, scm_print_state *pstate SCM_UNUSED)
76da80e7 1191{
9de87eea 1192 fat_mutex *m = SCM_MUTEX_DATA (mx);
0607ebbf 1193 scm_puts_unlocked ("#<mutex ", port);
9de87eea 1194 scm_uintprint ((scm_t_bits)m, 16, port);
0607ebbf 1195 scm_puts_unlocked (">", port);
9de87eea 1196 return 1;
76da80e7
MV
1197}
1198
76da80e7 1199static SCM
6180e336 1200make_fat_mutex (int recursive, int unchecked_unlock, int external_unlock)
76da80e7 1201{
9de87eea
MV
1202 fat_mutex *m;
1203 SCM mx;
fea65eb2 1204 scm_i_pthread_mutex_t lock = SCM_I_PTHREAD_MUTEX_INITIALIZER;
9de87eea
MV
1205
1206 m = scm_gc_malloc (sizeof (fat_mutex), "mutex");
fea65eb2
AW
1207 /* Because PTHREAD_MUTEX_INITIALIZER is static, it's plain old data,
1208 and so we can just copy it. */
1209 memcpy (&m->lock, &lock, sizeof (m->lock));
9de87eea 1210 m->owner = SCM_BOOL_F;
adc085f1 1211 m->level = 0;
6180e336 1212
adc085f1 1213 m->recursive = recursive;
6180e336
NJ
1214 m->unchecked_unlock = unchecked_unlock;
1215 m->allow_external_unlock = external_unlock;
1216
9de87eea
MV
1217 m->waiting = SCM_EOL;
1218 SCM_NEWSMOB (mx, scm_tc16_mutex, (scm_t_bits) m);
1219 m->waiting = make_queue ();
1220 return mx;
76da80e7
MV
1221}
1222
6180e336
NJ
1223SCM scm_make_mutex (void)
1224{
1225 return scm_make_mutex_with_flags (SCM_EOL);
1226}
1227
2a1d0688
NJ
1228SCM_SYMBOL (unchecked_unlock_sym, "unchecked-unlock");
1229SCM_SYMBOL (allow_external_unlock_sym, "allow-external-unlock");
1230SCM_SYMBOL (recursive_sym, "recursive");
6180e336
NJ
1231
1232SCM_DEFINE (scm_make_mutex_with_flags, "make-mutex", 0, 0, 1,
1233 (SCM flags),
9de87eea 1234 "Create a new mutex. ")
6180e336 1235#define FUNC_NAME s_scm_make_mutex_with_flags
76da80e7 1236{
6180e336
NJ
1237 int unchecked_unlock = 0, external_unlock = 0, recursive = 0;
1238
1239 SCM ptr = flags;
1240 while (! scm_is_null (ptr))
1241 {
1242 SCM flag = SCM_CAR (ptr);
1243 if (scm_is_eq (flag, unchecked_unlock_sym))
1244 unchecked_unlock = 1;
1245 else if (scm_is_eq (flag, allow_external_unlock_sym))
1246 external_unlock = 1;
1247 else if (scm_is_eq (flag, recursive_sym))
1248 recursive = 1;
74926120 1249 else
2a1d0688 1250 SCM_MISC_ERROR ("unsupported mutex option: ~a", scm_list_1 (flag));
6180e336
NJ
1251 ptr = SCM_CDR (ptr);
1252 }
1253 return make_fat_mutex (recursive, unchecked_unlock, external_unlock);
76da80e7
MV
1254}
1255#undef FUNC_NAME
1256
9de87eea 1257SCM_DEFINE (scm_make_recursive_mutex, "make-recursive-mutex", 0, 0, 0,
9bc4701c 1258 (void),
9de87eea
MV
1259 "Create a new recursive mutex. ")
1260#define FUNC_NAME s_scm_make_recursive_mutex
9bc4701c 1261{
6180e336 1262 return make_fat_mutex (1, 0, 0);
9bc4701c
MD
1263}
1264#undef FUNC_NAME
1265
6180e336
NJ
1266SCM_SYMBOL (scm_abandoned_mutex_error_key, "abandoned-mutex-error");
1267
1268static SCM
adc085f1 1269fat_mutex_lock (SCM mutex, scm_t_timespec *timeout, SCM owner, int *ret)
9de87eea
MV
1270{
1271 fat_mutex *m = SCM_MUTEX_DATA (mutex);
6180e336 1272
adc085f1 1273 SCM new_owner = SCM_UNBNDP (owner) ? scm_current_thread() : owner;
6180e336
NJ
1274 SCM err = SCM_BOOL_F;
1275
1276 struct timeval current_time;
9de87eea
MV
1277
1278 scm_i_scm_pthread_mutex_lock (&m->lock);
adc085f1
JG
1279
1280 while (1)
9de87eea 1281 {
adc085f1 1282 if (m->level == 0)
6180e336 1283 {
adc085f1 1284 m->owner = new_owner;
6180e336 1285 m->level++;
74926120 1286
adc085f1 1287 if (SCM_I_IS_THREAD (new_owner))
6180e336 1288 {
adc085f1 1289 scm_i_thread *t = SCM_I_THREAD_DATA (new_owner);
ccb80964 1290
4f39f31e
LC
1291 /* FIXME: The order in which `t->admin_mutex' and
1292 `m->lock' are taken differs from that in
1293 `on_thread_exit', potentially leading to deadlocks. */
6180e336 1294 scm_i_pthread_mutex_lock (&t->admin_mutex);
a0faf7dd
LC
1295
1296 /* Only keep a weak reference to MUTEX so that it's not
f57fdf07
LC
1297 retained when not referenced elsewhere (bug #27450).
1298 The weak pair itself is eventually removed when MUTEX
1299 is unlocked. Note that `t->mutexes' lists mutexes
1300 currently held by T, so it should be small. */
a141db86
AW
1301 t->mutexes = scm_cons (scm_make_weak_vector (SCM_INUM1, mutex),
1302 t->mutexes);
a0faf7dd 1303
6180e336 1304 scm_i_pthread_mutex_unlock (&t->admin_mutex);
6180e336 1305 }
adc085f1
JG
1306 *ret = 1;
1307 break;
1308 }
1309 else if (SCM_I_IS_THREAD (m->owner) && scm_c_thread_exited_p (m->owner))
1310 {
1311 m->owner = new_owner;
1312 err = scm_cons (scm_abandoned_mutex_error_key,
1313 scm_from_locale_string ("lock obtained on abandoned "
1314 "mutex"));
1315 *ret = 1;
1316 break;
1317 }
1318 else if (scm_is_eq (m->owner, new_owner))
1319 {
1320 if (m->recursive)
1321 {
1322 m->level++;
74926120 1323 *ret = 1;
adc085f1
JG
1324 }
1325 else
6180e336 1326 {
adc085f1
JG
1327 err = scm_cons (scm_misc_error_key,
1328 scm_from_locale_string ("mutex already locked "
1329 "by thread"));
1330 *ret = 0;
1331 }
74926120 1332 break;
adc085f1 1333 }
9de87eea 1334 else
9de87eea 1335 {
74926120 1336 if (timeout != NULL)
adc085f1
JG
1337 {
1338 gettimeofday (&current_time, NULL);
1339 if (current_time.tv_sec > timeout->tv_sec ||
1340 (current_time.tv_sec == timeout->tv_sec &&
1341 current_time.tv_usec * 1000 > timeout->tv_nsec))
6180e336 1342 {
adc085f1
JG
1343 *ret = 0;
1344 break;
6180e336 1345 }
6180e336 1346 }
37a52039 1347 block_self (m->waiting, mutex, &m->lock, timeout);
9de87eea
MV
1348 scm_i_pthread_mutex_unlock (&m->lock);
1349 SCM_TICK;
1350 scm_i_scm_pthread_mutex_lock (&m->lock);
1351 }
1352 }
1353 scm_i_pthread_mutex_unlock (&m->lock);
6180e336 1354 return err;
9de87eea
MV
1355}
1356
6180e336
NJ
1357SCM scm_lock_mutex (SCM mx)
1358{
adc085f1 1359 return scm_lock_mutex_timed (mx, SCM_UNDEFINED, SCM_UNDEFINED);
6180e336
NJ
1360}
1361
adc085f1
JG
1362SCM_DEFINE (scm_lock_mutex_timed, "lock-mutex", 1, 2, 0,
1363 (SCM m, SCM timeout, SCM owner),
b7e64f8b
BT
1364 "Lock mutex @var{m}. If the mutex is already locked, the calling\n"
1365 "thread blocks until the mutex becomes available. The function\n"
1366 "returns when the calling thread owns the lock on @var{m}.\n"
1367 "Locking a mutex that a thread already owns will succeed right\n"
1368 "away and will not block the thread. That is, Guile's mutexes\n"
1369 "are @emph{recursive}.")
6180e336 1370#define FUNC_NAME s_scm_lock_mutex_timed
9bc4701c 1371{
6180e336
NJ
1372 SCM exception;
1373 int ret = 0;
1374 scm_t_timespec cwaittime, *waittime = NULL;
76da80e7 1375
6180e336
NJ
1376 SCM_VALIDATE_MUTEX (1, m);
1377
1378 if (! SCM_UNBNDP (timeout) && ! scm_is_false (timeout))
1379 {
1380 to_timespec (timeout, &cwaittime);
1381 waittime = &cwaittime;
1382 }
1383
97ec95b7
LC
1384 if (!SCM_UNBNDP (owner) && !scm_is_false (owner))
1385 SCM_VALIDATE_THREAD (3, owner);
1386
adc085f1 1387 exception = fat_mutex_lock (m, waittime, owner, &ret);
6180e336
NJ
1388 if (!scm_is_false (exception))
1389 scm_ithrow (SCM_CAR (exception), scm_list_1 (SCM_CDR (exception)), 1);
1390 return ret ? SCM_BOOL_T : SCM_BOOL_F;
9bc4701c 1391}
76da80e7 1392#undef FUNC_NAME
9bc4701c 1393
9ec1573d
MW
1394static void
1395lock_mutex_return_void (SCM mx)
1396{
1397 (void) scm_lock_mutex (mx);
1398}
1399
1400static void
1401unlock_mutex_return_void (SCM mx)
1402{
1403 (void) scm_unlock_mutex (mx);
1404}
1405
a4d106c7 1406void
661ae7ab 1407scm_dynwind_lock_mutex (SCM mutex)
a4d106c7 1408{
9ec1573d 1409 scm_dynwind_unwind_handler_with_scm (unlock_mutex_return_void, mutex,
661ae7ab 1410 SCM_F_WIND_EXPLICITLY);
9ec1573d 1411 scm_dynwind_rewind_handler_with_scm (lock_mutex_return_void, mutex,
661ae7ab 1412 SCM_F_WIND_EXPLICITLY);
a4d106c7
MV
1413}
1414
9bc4701c 1415SCM_DEFINE (scm_try_mutex, "try-mutex", 1, 0, 0,
ba1b7223 1416 (SCM mutex),
9bc4701c
MD
1417"Try to lock @var{mutex}. If the mutex is already locked by someone "
1418"else, return @code{#f}. Else lock the mutex and return @code{#t}. ")
1419#define FUNC_NAME s_scm_try_mutex
1420{
6180e336
NJ
1421 SCM exception;
1422 int ret = 0;
1423 scm_t_timespec cwaittime, *waittime = NULL;
9de87eea 1424
ba1b7223 1425 SCM_VALIDATE_MUTEX (1, mutex);
6180e336
NJ
1426
1427 to_timespec (scm_from_int(0), &cwaittime);
1428 waittime = &cwaittime;
74926120 1429
adc085f1 1430 exception = fat_mutex_lock (mutex, waittime, SCM_UNDEFINED, &ret);
6180e336
NJ
1431 if (!scm_is_false (exception))
1432 scm_ithrow (SCM_CAR (exception), scm_list_1 (SCM_CDR (exception)), 1);
1433 return ret ? SCM_BOOL_T : SCM_BOOL_F;
9de87eea
MV
1434}
1435#undef FUNC_NAME
76da80e7 1436
6180e336
NJ
1437/*** Fat condition variables */
1438
1439typedef struct {
1440 scm_i_pthread_mutex_t lock;
1441 SCM waiting; /* the threads waiting for this condition. */
1442} fat_cond;
1443
1444#define SCM_CONDVARP(x) SCM_SMOB_PREDICATE (scm_tc16_condvar, x)
1445#define SCM_CONDVAR_DATA(x) ((fat_cond *) SCM_SMOB_DATA (x))
1446
a141db86
AW
1447static void
1448remove_mutex_from_thread (SCM mutex, scm_i_thread *t)
1449{
1450 SCM walk, prev;
1451
1452 for (prev = SCM_BOOL_F, walk = t->mutexes; scm_is_pair (walk);
1453 walk = SCM_CDR (walk))
1454 {
1455 if (scm_is_eq (mutex, scm_c_weak_vector_ref (SCM_CAR (walk), 0)))
1456 {
1457 if (scm_is_pair (prev))
1458 SCM_SETCDR (prev, SCM_CDR (walk));
1459 else
1460 t->mutexes = SCM_CDR (walk);
1461 break;
1462 }
1463 }
1464}
1465
6180e336
NJ
1466static int
1467fat_mutex_unlock (SCM mutex, SCM cond,
1468 const scm_t_timespec *waittime, int relock)
9de87eea 1469{
7f991c7d 1470 SCM owner;
6180e336
NJ
1471 fat_mutex *m = SCM_MUTEX_DATA (mutex);
1472 fat_cond *c = NULL;
1473 scm_i_thread *t = SCM_I_CURRENT_THREAD;
1474 int err = 0, ret = 0;
9de87eea
MV
1475
1476 scm_i_scm_pthread_mutex_lock (&m->lock);
adc085f1 1477
7f991c7d 1478 owner = m->owner;
adc085f1 1479
d31ae2c3 1480 if (!scm_is_eq (owner, t->handle))
9bc4701c 1481 {
adc085f1 1482 if (m->level == 0)
6180e336
NJ
1483 {
1484 if (!m->unchecked_unlock)
2a1d0688
NJ
1485 {
1486 scm_i_pthread_mutex_unlock (&m->lock);
1487 scm_misc_error (NULL, "mutex not locked", SCM_EOL);
1488 }
d31ae2c3 1489 owner = t->handle;
6180e336
NJ
1490 }
1491 else if (!m->allow_external_unlock)
2a1d0688
NJ
1492 {
1493 scm_i_pthread_mutex_unlock (&m->lock);
1494 scm_misc_error (NULL, "mutex not locked by current thread", SCM_EOL);
1495 }
6180e336
NJ
1496 }
1497
1498 if (! (SCM_UNBNDP (cond)))
1499 {
6180e336
NJ
1500 c = SCM_CONDVAR_DATA (cond);
1501 while (1)
1502 {
1503 int brk = 0;
1504
6180e336
NJ
1505 if (m->level > 0)
1506 m->level--;
adc085f1 1507 if (m->level == 0)
f57fdf07
LC
1508 {
1509 /* Change the owner of MUTEX. */
a141db86 1510 remove_mutex_from_thread (mutex, t);
f57fdf07
LC
1511 m->owner = unblock_from_queue (m->waiting);
1512 }
adc085f1 1513
6180e336 1514 t->block_asyncs++;
74926120 1515
d2a51087
NJ
1516 err = block_self (c->waiting, cond, &m->lock, waittime);
1517 scm_i_pthread_mutex_unlock (&m->lock);
6180e336
NJ
1518
1519 if (err == 0)
1520 {
1521 ret = 1;
1522 brk = 1;
1523 }
1524 else if (err == ETIMEDOUT)
1525 {
1526 ret = 0;
1527 brk = 1;
1528 }
1529 else if (err != EINTR)
74926120 1530 {
6180e336 1531 errno = err;
6180e336 1532 scm_syserror (NULL);
74926120 1533 }
6180e336
NJ
1534
1535 if (brk)
1536 {
1537 if (relock)
adc085f1 1538 scm_lock_mutex_timed (mutex, SCM_UNDEFINED, owner);
4201062d 1539 t->block_asyncs--;
6180e336
NJ
1540 break;
1541 }
74926120 1542
6180e336 1543 t->block_asyncs--;
27c6ebcb 1544 scm_async_tick ();
74926120 1545
6180e336
NJ
1546 scm_remember_upto_here_2 (cond, mutex);
1547
1548 scm_i_scm_pthread_mutex_lock (&m->lock);
1549 }
9bc4701c 1550 }
9de87eea 1551 else
6180e336
NJ
1552 {
1553 if (m->level > 0)
1554 m->level--;
74926120 1555 if (m->level == 0)
f57fdf07
LC
1556 {
1557 /* Change the owner of MUTEX. */
a141db86 1558 remove_mutex_from_thread (mutex, t);
f57fdf07
LC
1559 m->owner = unblock_from_queue (m->waiting);
1560 }
74926120 1561
6180e336
NJ
1562 scm_i_pthread_mutex_unlock (&m->lock);
1563 ret = 1;
1564 }
9de87eea 1565
6180e336 1566 return ret;
9bc4701c 1567}
9bc4701c 1568
6180e336
NJ
1569SCM scm_unlock_mutex (SCM mx)
1570{
1571 return scm_unlock_mutex_timed (mx, SCM_UNDEFINED, SCM_UNDEFINED);
9bc4701c 1572}
9bc4701c 1573
6180e336
NJ
1574SCM_DEFINE (scm_unlock_mutex_timed, "unlock-mutex", 1, 2, 0,
1575 (SCM mx, SCM cond, SCM timeout),
9bc4701c
MD
1576"Unlocks @var{mutex} if the calling thread owns the lock on "
1577"@var{mutex}. Calling unlock-mutex on a mutex not owned by the current "
1578"thread results in undefined behaviour. Once a mutex has been unlocked, "
1579"one thread blocked on @var{mutex} is awakened and grabs the mutex "
1580"lock. Every call to @code{lock-mutex} by this thread must be matched "
1581"with a call to @code{unlock-mutex}. Only the last call to "
1582"@code{unlock-mutex} will actually unlock the mutex. ")
6180e336 1583#define FUNC_NAME s_scm_unlock_mutex_timed
9bc4701c 1584{
6180e336
NJ
1585 scm_t_timespec cwaittime, *waittime = NULL;
1586
9bc4701c 1587 SCM_VALIDATE_MUTEX (1, mx);
6180e336
NJ
1588 if (! (SCM_UNBNDP (cond)))
1589 {
1590 SCM_VALIDATE_CONDVAR (2, cond);
1591
2a1d8403 1592 if (! SCM_UNBNDP (timeout) && ! scm_is_false (timeout))
6180e336
NJ
1593 {
1594 to_timespec (timeout, &cwaittime);
1595 waittime = &cwaittime;
1596 }
1597 }
1598
1599 return fat_mutex_unlock (mx, cond, waittime, 0) ? SCM_BOOL_T : SCM_BOOL_F;
9bc4701c
MD
1600}
1601#undef FUNC_NAME
1602
6180e336
NJ
1603SCM_DEFINE (scm_mutex_p, "mutex?", 1, 0, 0,
1604 (SCM obj),
1605 "Return @code{#t} if @var{obj} is a mutex.")
1606#define FUNC_NAME s_scm_mutex_p
1607{
1608 return SCM_MUTEXP (obj) ? SCM_BOOL_T : SCM_BOOL_F;
1609}
74926120 1610#undef FUNC_NAME
9de87eea
MV
1611
1612SCM_DEFINE (scm_mutex_owner, "mutex-owner", 1, 0, 0,
1613 (SCM mx),
1614 "Return the thread owning @var{mx}, or @code{#f}.")
1615#define FUNC_NAME s_scm_mutex_owner
1616{
adc085f1
JG
1617 SCM owner;
1618 fat_mutex *m = NULL;
1619
9de87eea 1620 SCM_VALIDATE_MUTEX (1, mx);
adc085f1
JG
1621 m = SCM_MUTEX_DATA (mx);
1622 scm_i_pthread_mutex_lock (&m->lock);
1623 owner = m->owner;
1624 scm_i_pthread_mutex_unlock (&m->lock);
1625
1626 return owner;
9de87eea
MV
1627}
1628#undef FUNC_NAME
1629
1630SCM_DEFINE (scm_mutex_level, "mutex-level", 1, 0, 0,
1631 (SCM mx),
adc085f1 1632 "Return the lock level of mutex @var{mx}.")
9de87eea
MV
1633#define FUNC_NAME s_scm_mutex_level
1634{
1635 SCM_VALIDATE_MUTEX (1, mx);
1636 return scm_from_int (SCM_MUTEX_DATA(mx)->level);
1637}
1638#undef FUNC_NAME
1639
adc085f1
JG
1640SCM_DEFINE (scm_mutex_locked_p, "mutex-locked?", 1, 0, 0,
1641 (SCM mx),
1642 "Returns @code{#t} if the mutex @var{mx} is locked.")
1643#define FUNC_NAME s_scm_mutex_locked_p
1644{
1645 SCM_VALIDATE_MUTEX (1, mx);
1646 return SCM_MUTEX_DATA (mx)->level > 0 ? SCM_BOOL_T : SCM_BOOL_F;
1647}
1648#undef FUNC_NAME
9de87eea 1649
9de87eea
MV
1650static int
1651fat_cond_print (SCM cv, SCM port, scm_print_state *pstate SCM_UNUSED)
1652{
1653 fat_cond *c = SCM_CONDVAR_DATA (cv);
0607ebbf 1654 scm_puts_unlocked ("#<condition-variable ", port);
9de87eea 1655 scm_uintprint ((scm_t_bits)c, 16, port);
0607ebbf 1656 scm_puts_unlocked (">", port);
9de87eea
MV
1657 return 1;
1658}
9bc4701c 1659
d823b11b
MV
1660SCM_DEFINE (scm_make_condition_variable, "make-condition-variable", 0, 0, 0,
1661 (void),
1662 "Make a new condition variable.")
1663#define FUNC_NAME s_scm_make_condition_variable
5f05c406 1664{
9de87eea
MV
1665 fat_cond *c;
1666 SCM cv;
1667
1668 c = scm_gc_malloc (sizeof (fat_cond), "condition variable");
9de87eea
MV
1669 c->waiting = SCM_EOL;
1670 SCM_NEWSMOB (cv, scm_tc16_condvar, (scm_t_bits) c);
1671 c->waiting = make_queue ();
d823b11b 1672 return cv;
5f05c406 1673}
d823b11b 1674#undef FUNC_NAME
5f05c406 1675
d823b11b
MV
1676SCM_DEFINE (scm_timed_wait_condition_variable, "wait-condition-variable", 2, 1, 0,
1677 (SCM cv, SCM mx, SCM t),
b7e64f8b
BT
1678"Wait until condition variable @var{cv} has been signalled. While waiting, "
1679"mutex @var{mx} is atomically unlocked (as with @code{unlock-mutex}) and "
1680"is locked again when this function returns. When @var{t} is given, "
d823b11b
MV
1681"it specifies a point in time where the waiting should be aborted. It "
1682"can be either a integer as returned by @code{current-time} or a pair "
1683"as returned by @code{gettimeofday}. When the waiting is aborted the "
1684"mutex is locked and @code{#f} is returned. When the condition "
1685"variable is in fact signalled, the mutex is also locked and @code{#t} "
1686"is returned. ")
1687#define FUNC_NAME s_scm_timed_wait_condition_variable
5f05c406 1688{
9de87eea 1689 scm_t_timespec waittime, *waitptr = NULL;
d823b11b
MV
1690
1691 SCM_VALIDATE_CONDVAR (1, cv);
1692 SCM_VALIDATE_MUTEX (2, mx);
74926120 1693
d823b11b
MV
1694 if (!SCM_UNBNDP (t))
1695 {
6180e336 1696 to_timespec (t, &waittime);
9de87eea 1697 waitptr = &waittime;
d823b11b
MV
1698 }
1699
2a1d0688 1700 return fat_mutex_unlock (mx, cv, waitptr, 1) ? SCM_BOOL_T : SCM_BOOL_F;
5f05c406 1701}
d823b11b 1702#undef FUNC_NAME
5f05c406 1703
9de87eea
MV
1704static void
1705fat_cond_signal (fat_cond *c)
1706{
9de87eea 1707 unblock_from_queue (c->waiting);
9de87eea
MV
1708}
1709
d823b11b
MV
1710SCM_DEFINE (scm_signal_condition_variable, "signal-condition-variable", 1, 0, 0,
1711 (SCM cv),
1712 "Wake up one thread that is waiting for @var{cv}")
1713#define FUNC_NAME s_scm_signal_condition_variable
5f05c406 1714{
d823b11b 1715 SCM_VALIDATE_CONDVAR (1, cv);
9de87eea 1716 fat_cond_signal (SCM_CONDVAR_DATA (cv));
d823b11b 1717 return SCM_BOOL_T;
5f05c406 1718}
d823b11b 1719#undef FUNC_NAME
5f05c406 1720
9de87eea
MV
1721static void
1722fat_cond_broadcast (fat_cond *c)
1723{
9de87eea
MV
1724 while (scm_is_true (unblock_from_queue (c->waiting)))
1725 ;
9de87eea
MV
1726}
1727
d823b11b
MV
1728SCM_DEFINE (scm_broadcast_condition_variable, "broadcast-condition-variable", 1, 0, 0,
1729 (SCM cv),
1730 "Wake up all threads that are waiting for @var{cv}. ")
1731#define FUNC_NAME s_scm_broadcast_condition_variable
5f05c406 1732{
d823b11b 1733 SCM_VALIDATE_CONDVAR (1, cv);
9de87eea 1734 fat_cond_broadcast (SCM_CONDVAR_DATA (cv));
d823b11b 1735 return SCM_BOOL_T;
5f05c406 1736}
d823b11b 1737#undef FUNC_NAME
5f05c406 1738
6180e336
NJ
1739SCM_DEFINE (scm_condition_variable_p, "condition-variable?", 1, 0, 0,
1740 (SCM obj),
1741 "Return @code{#t} if @var{obj} is a condition variable.")
1742#define FUNC_NAME s_scm_condition_variable_p
1743{
1744 return SCM_CONDVARP(obj) ? SCM_BOOL_T : SCM_BOOL_F;
1745}
1746#undef FUNC_NAME
1747
6087fad9 1748
8c2b3143 1749\f
d823b11b
MV
1750/*** Select */
1751
8c2b3143
LC
1752struct select_args
1753{
1754 int nfds;
6ab4de61
AW
1755 fd_set *read_fds;
1756 fd_set *write_fds;
1757 fd_set *except_fds;
8c2b3143
LC
1758 struct timeval *timeout;
1759
1760 int result;
1761 int errno_value;
1762};
1763
1764static void *
1765do_std_select (void *args)
1766{
1767 struct select_args *select_args;
1768
1769 select_args = (struct select_args *) args;
1770
1771 select_args->result =
1772 select (select_args->nfds,
1773 select_args->read_fds, select_args->write_fds,
1774 select_args->except_fds, select_args->timeout);
1775 select_args->errno_value = errno;
1776
1777 return NULL;
1778}
1779
911782b7 1780int
9de87eea 1781scm_std_select (int nfds,
6ab4de61
AW
1782 fd_set *readfds,
1783 fd_set *writefds,
1784 fd_set *exceptfds,
9de87eea
MV
1785 struct timeval *timeout)
1786{
1787 fd_set my_readfds;
1788 int res, eno, wakeup_fd;
1789 scm_i_thread *t = SCM_I_CURRENT_THREAD;
8c2b3143 1790 struct select_args args;
9de87eea
MV
1791
1792 if (readfds == NULL)
1793 {
1794 FD_ZERO (&my_readfds);
1795 readfds = &my_readfds;
1796 }
1797
1798 while (scm_i_setup_sleep (t, SCM_BOOL_F, NULL, t->sleep_pipe[1]))
1799 SCM_TICK;
1800
1801 wakeup_fd = t->sleep_pipe[0];
9de87eea
MV
1802 FD_SET (wakeup_fd, readfds);
1803 if (wakeup_fd >= nfds)
1804 nfds = wakeup_fd+1;
9de87eea 1805
8c2b3143
LC
1806 args.nfds = nfds;
1807 args.read_fds = readfds;
1808 args.write_fds = writefds;
1809 args.except_fds = exceptfds;
1810 args.timeout = timeout;
1811
1812 /* Explicitly cooperate with the GC. */
1813 scm_without_guile (do_std_select, &args);
1814
1815 res = args.result;
1816 eno = args.errno_value;
1817
1818 t->sleep_fd = -1;
9de87eea
MV
1819 scm_i_reset_sleep (t);
1820
1821 if (res > 0 && FD_ISSET (wakeup_fd, readfds))
1822 {
1823 char dummy;
634aa8de
LC
1824 full_read (wakeup_fd, &dummy, 1);
1825
9de87eea
MV
1826 FD_CLR (wakeup_fd, readfds);
1827 res -= 1;
1828 if (res == 0)
1829 {
1830 eno = EINTR;
1831 res = -1;
1832 }
1833 }
d823b11b
MV
1834 errno = eno;
1835 return res;
5f05c406
MV
1836}
1837
9de87eea 1838/* Convenience API for blocking while in guile mode. */
76da80e7 1839
9de87eea 1840#if SCM_USE_PTHREAD_THREADS
92e64b87 1841
2956b071
LC
1842/* It seems reasonable to not run procedures related to mutex and condition
1843 variables within `GC_do_blocking ()' since, (i) the GC can operate even
1844 without it, and (ii) the only potential gain would be GC latency. See
1845 http://thread.gmane.org/gmane.comp.programming.garbage-collection.boehmgc/2245/focus=2251
1846 for a discussion of the pros and cons. */
1847
9bc4701c 1848int
9de87eea 1849scm_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
9bc4701c 1850{
9de87eea 1851 int res = scm_i_pthread_mutex_lock (mutex);
9bc4701c
MD
1852 return res;
1853}
1854
9de87eea 1855static void
2b829bbb 1856do_unlock (void *data)
28d52ebb 1857{
9de87eea 1858 scm_i_pthread_mutex_unlock ((scm_i_pthread_mutex_t *)data);
28d52ebb
MD
1859}
1860
1861void
661ae7ab 1862scm_dynwind_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
28d52ebb 1863{
9de87eea 1864 scm_i_scm_pthread_mutex_lock (mutex);
2b829bbb 1865 scm_dynwind_unwind_handler (do_unlock, mutex, SCM_F_WIND_EXPLICITLY);
28d52ebb
MD
1866}
1867
9bc4701c 1868int
9de87eea 1869scm_pthread_cond_wait (scm_i_pthread_cond_t *cond, scm_i_pthread_mutex_t *mutex)
9bc4701c 1870{
4cf72f0b
LC
1871 int res;
1872 scm_i_thread *t = SCM_I_CURRENT_THREAD;
1873
1874 t->held_mutex = mutex;
1875 res = scm_i_pthread_cond_wait (cond, mutex);
1876 t->held_mutex = NULL;
1877
9bc4701c
MD
1878 return res;
1879}
9bc4701c 1880
76da80e7 1881int
9de87eea
MV
1882scm_pthread_cond_timedwait (scm_i_pthread_cond_t *cond,
1883 scm_i_pthread_mutex_t *mutex,
1884 const scm_t_timespec *wt)
76da80e7 1885{
4cf72f0b
LC
1886 int res;
1887 scm_i_thread *t = SCM_I_CURRENT_THREAD;
1888
1889 t->held_mutex = mutex;
1890 res = scm_i_pthread_cond_timedwait (cond, mutex, wt);
1891 t->held_mutex = NULL;
1892
9de87eea 1893 return res;
76da80e7
MV
1894}
1895
9de87eea 1896#endif
76da80e7 1897
e676a4c3
MW
1898static void
1899do_unlock_with_asyncs (void *data)
1900{
1901 scm_i_pthread_mutex_unlock ((scm_i_pthread_mutex_t *)data);
1902 SCM_I_CURRENT_THREAD->block_asyncs--;
1903}
1904
1905void
1906scm_i_dynwind_pthread_mutex_lock_block_asyncs (scm_i_pthread_mutex_t *mutex)
1907{
1908 SCM_I_CURRENT_THREAD->block_asyncs++;
1909 scm_i_scm_pthread_mutex_lock (mutex);
1910 scm_dynwind_unwind_handler (do_unlock_with_asyncs, mutex,
1911 SCM_F_WIND_EXPLICITLY);
1912}
1913
d823b11b 1914unsigned long
9de87eea 1915scm_std_usleep (unsigned long usecs)
5f05c406 1916{
d823b11b
MV
1917 struct timeval tv;
1918 tv.tv_usec = usecs % 1000000;
1919 tv.tv_sec = usecs / 1000000;
9de87eea
MV
1920 scm_std_select (0, NULL, NULL, NULL, &tv);
1921 return tv.tv_sec * 1000000 + tv.tv_usec;
5f05c406
MV
1922}
1923
9de87eea
MV
1924unsigned int
1925scm_std_sleep (unsigned int secs)
6c214b62 1926{
d823b11b
MV
1927 struct timeval tv;
1928 tv.tv_usec = 0;
1929 tv.tv_sec = secs;
9de87eea 1930 scm_std_select (0, NULL, NULL, NULL, &tv);
d823b11b 1931 return tv.tv_sec;
6c214b62
MD
1932}
1933
d823b11b
MV
1934/*** Misc */
1935
1936SCM_DEFINE (scm_current_thread, "current-thread", 0, 0, 0,
1937 (void),
1938 "Return the thread that called this function.")
1939#define FUNC_NAME s_scm_current_thread
1940{
9de87eea 1941 return SCM_I_CURRENT_THREAD->handle;
d823b11b
MV
1942}
1943#undef FUNC_NAME
1944
9de87eea
MV
1945static SCM
1946scm_c_make_list (size_t n, SCM fill)
1947{
1948 SCM res = SCM_EOL;
1949 while (n-- > 0)
1950 res = scm_cons (fill, res);
1951 return res;
1952}
1953
d823b11b
MV
1954SCM_DEFINE (scm_all_threads, "all-threads", 0, 0, 0,
1955 (void),
1956 "Return a list of all threads.")
9bc4701c 1957#define FUNC_NAME s_scm_all_threads
d823b11b 1958{
9de87eea
MV
1959 /* We can not allocate while holding the thread_admin_mutex because
1960 of the way GC is done.
1961 */
1962 int n = thread_count;
1963 scm_i_thread *t;
1964 SCM list = scm_c_make_list (n, SCM_UNSPECIFIED), *l;
d823b11b 1965
9de87eea
MV
1966 scm_i_pthread_mutex_lock (&thread_admin_mutex);
1967 l = &list;
1968 for (t = all_threads; t && n > 0; t = t->next_thread)
1969 {
2e77f720
LC
1970 if (t != scm_i_signal_delivery_thread)
1971 {
1972 SCM_SETCAR (*l, t->handle);
1973 l = SCM_CDRLOC (*l);
1974 }
9de87eea
MV
1975 n--;
1976 }
1977 *l = SCM_EOL;
1978 scm_i_pthread_mutex_unlock (&thread_admin_mutex);
1979 return list;
d823b11b 1980}
9de87eea 1981#undef FUNC_NAME
d823b11b
MV
1982
1983SCM_DEFINE (scm_thread_exited_p, "thread-exited?", 1, 0, 0,
1984 (SCM thread),
1985 "Return @code{#t} iff @var{thread} has exited.\n")
1986#define FUNC_NAME s_scm_thread_exited_p
1987{
7888309b 1988 return scm_from_bool (scm_c_thread_exited_p (thread));
d823b11b
MV
1989}
1990#undef FUNC_NAME
1991
911782b7 1992int
d823b11b
MV
1993scm_c_thread_exited_p (SCM thread)
1994#define FUNC_NAME s_scm_thread_exited_p
5f05c406 1995{
9de87eea 1996 scm_i_thread *t;
d823b11b 1997 SCM_VALIDATE_THREAD (1, thread);
9de87eea 1998 t = SCM_I_THREAD_DATA (thread);
d823b11b 1999 return t->exited;
5f05c406 2000}
d823b11b 2001#undef FUNC_NAME
5f05c406 2002
d20912e6
LC
2003SCM_DEFINE (scm_total_processor_count, "total-processor-count", 0, 0, 0,
2004 (void),
2005 "Return the total number of processors of the machine, which\n"
2006 "is guaranteed to be at least 1. A ``processor'' here is a\n"
2007 "thread execution unit, which can be either:\n\n"
2008 "@itemize\n"
2009 "@item an execution core in a (possibly multi-core) chip, in a\n"
2010 " (possibly multi- chip) module, in a single computer, or\n"
2011 "@item a thread execution unit inside a core in the case of\n"
2012 " @dfn{hyper-threaded} CPUs.\n"
2013 "@end itemize\n\n"
2014 "Which of the two definitions is used, is unspecified.\n")
2015#define FUNC_NAME s_scm_total_processor_count
2016{
2017 return scm_from_ulong (num_processors (NPROC_ALL));
2018}
2019#undef FUNC_NAME
2020
2021SCM_DEFINE (scm_current_processor_count, "current-processor-count", 0, 0, 0,
2022 (void),
2023 "Like @code{total-processor-count}, but return the number of\n"
2024 "processors available to the current process. See\n"
2025 "@code{setaffinity} and @code{getaffinity} for more\n"
2026 "information.\n")
2027#define FUNC_NAME s_scm_current_processor_count
2028{
2029 return scm_from_ulong (num_processors (NPROC_CURRENT));
2030}
2031#undef FUNC_NAME
2032
2033
2034\f
2035
9de87eea 2036static scm_i_pthread_cond_t wake_up_cond;
9bc4701c
MD
2037static int threads_initialized_p = 0;
2038
9bc4701c 2039
a4d106c7
MV
2040/* This mutex is used by SCM_CRITICAL_SECTION_START/END.
2041 */
d1138028 2042scm_i_pthread_mutex_t scm_i_critical_section_mutex;
a4d106c7 2043
661ae7ab 2044static SCM dynwind_critical_section_mutex;
a54a94b3 2045
9bc4701c 2046void
661ae7ab 2047scm_dynwind_critical_section (SCM mutex)
76da80e7 2048{
a4d106c7 2049 if (scm_is_false (mutex))
661ae7ab
MV
2050 mutex = dynwind_critical_section_mutex;
2051 scm_dynwind_lock_mutex (mutex);
2052 scm_dynwind_block_asyncs ();
9de87eea
MV
2053}
2054
2055/*** Initialization */
2056
9de87eea
MV
2057scm_i_pthread_mutex_t scm_i_misc_mutex;
2058
d1138028
MV
2059#if SCM_USE_PTHREAD_THREADS
2060pthread_mutexattr_t scm_i_pthread_mutexattr_recursive[1];
2061#endif
2062
9de87eea 2063void
12c1d861 2064scm_threads_prehistory (void *base)
9de87eea 2065{
d1138028
MV
2066#if SCM_USE_PTHREAD_THREADS
2067 pthread_mutexattr_init (scm_i_pthread_mutexattr_recursive);
2068 pthread_mutexattr_settype (scm_i_pthread_mutexattr_recursive,
2069 PTHREAD_MUTEX_RECURSIVE);
2070#endif
2071
2072 scm_i_pthread_mutex_init (&scm_i_critical_section_mutex,
2073 scm_i_pthread_mutexattr_recursive);
9de87eea
MV
2074 scm_i_pthread_mutex_init (&scm_i_misc_mutex, NULL);
2075 scm_i_pthread_cond_init (&wake_up_cond, NULL);
74926120 2076
7af0c3b3
AW
2077 thread_gc_kind =
2078 GC_new_kind (GC_new_free_list (),
2079 GC_MAKE_PROC (GC_new_proc (thread_mark), 0),
2080 0, 1);
2081
12c1d861 2082 guilify_self_1 ((struct GC_stack_base *) base);
9bc4701c
MD
2083}
2084
d823b11b
MV
2085scm_t_bits scm_tc16_thread;
2086scm_t_bits scm_tc16_mutex;
2087scm_t_bits scm_tc16_condvar;
7bfd3b9e 2088
7bfd3b9e 2089void
9de87eea 2090scm_init_threads ()
7bfd3b9e 2091{
9de87eea 2092 scm_tc16_thread = scm_make_smob_type ("thread", sizeof (scm_i_thread));
d823b11b 2093 scm_set_smob_print (scm_tc16_thread, thread_print);
d823b11b 2094
9de87eea 2095 scm_tc16_mutex = scm_make_smob_type ("mutex", sizeof (fat_mutex));
9de87eea 2096 scm_set_smob_print (scm_tc16_mutex, fat_mutex_print);
9bc4701c 2097
9de87eea
MV
2098 scm_tc16_condvar = scm_make_smob_type ("condition-variable",
2099 sizeof (fat_cond));
9de87eea 2100 scm_set_smob_print (scm_tc16_condvar, fat_cond_print);
d823b11b 2101
9de87eea
MV
2102 scm_i_default_dynamic_state = SCM_BOOL_F;
2103 guilify_self_2 (SCM_BOOL_F);
9bc4701c 2104 threads_initialized_p = 1;
a4d106c7 2105
f39448c5 2106 dynwind_critical_section_mutex = scm_make_recursive_mutex ();
7bfd3b9e 2107}
89e00824 2108
5f05c406 2109void
9de87eea 2110scm_init_threads_default_dynamic_state ()
5f05c406 2111{
9de87eea 2112 SCM state = scm_make_dynamic_state (scm_current_dynamic_state ());
f39448c5 2113 scm_i_default_dynamic_state = state;
5f05c406
MV
2114}
2115
d823b11b 2116void
9de87eea 2117scm_init_thread_procs ()
d823b11b 2118{
9de87eea 2119#include "libguile/threads.x"
d823b11b
MV
2120}
2121
3c13664e
LC
2122\f
2123/* IA64-specific things. */
2124
2125#ifdef __ia64__
2126# ifdef __hpux
2127# include <sys/param.h>
2128# include <sys/pstat.h>
2129void *
2130scm_ia64_register_backing_store_base (void)
2131{
2132 struct pst_vm_status vm_status;
2133 int i = 0;
2134 while (pstat_getprocvm (&vm_status, sizeof (vm_status), 0, i++) == 1)
2135 if (vm_status.pst_type == PS_RSESTACK)
2136 return (void *) vm_status.pst_vaddr;
2137 abort ();
2138}
2139void *
2140scm_ia64_ar_bsp (const void *ctx)
2141{
2142 uint64_t bsp;
2143 __uc_get_ar_bsp (ctx, &bsp);
2144 return (void *) bsp;
2145}
2146# endif /* hpux */
2147# ifdef linux
2148# include <ucontext.h>
2149void *
2150scm_ia64_register_backing_store_base (void)
2151{
2152 extern void *__libc_ia64_register_backing_store_base;
2153 return __libc_ia64_register_backing_store_base;
2154}
2155void *
2156scm_ia64_ar_bsp (const void *opaque)
2157{
2158 const ucontext_t *ctx = opaque;
2159 return (void *) ctx->uc_mcontext.sc_ar_bsp;
2160}
2161# endif /* linux */
ba20d262
AW
2162# ifdef __FreeBSD__
2163# include <ucontext.h>
2164void *
2165scm_ia64_register_backing_store_base (void)
2166{
2167 return (void *)0x8000000000000000;
2168}
2169void *
2170scm_ia64_ar_bsp (const void *opaque)
2171{
2172 const ucontext_t *ctx = opaque;
2173 return (void *)(ctx->uc_mcontext.mc_special.bspstore
2174 + ctx->uc_mcontext.mc_special.ndirty);
2175}
2176# endif /* __FreeBSD__ */
3c13664e
LC
2177#endif /* __ia64__ */
2178
2179
89e00824
ML
2180/*
2181 Local Variables:
2182 c-file-style: "gnu"
2183 End:
2184*/