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