* dynl-dl.c (sysdep_dynl_link): Added parenthesis around the
[bpt/guile.git] / libguile / coop-threads.c
CommitLineData
d90ca38d 1/* Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
7bfd3b9e
JB
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
82892bed
JB
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
7bfd3b9e
JB
17 *
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
20 *
21 * The exception is that, if you link the GUILE library with other files
22 * to produce an executable, this does not by itself cause the
23 * resulting executable to be covered by the GNU General Public License.
24 * Your use of that executable is in no way restricted on account of
25 * linking the GUILE library code into it.
26 *
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
29 *
30 * This exception applies only to the code released by the
31 * Free Software Foundation under the name GUILE. If you copy
32 * code from other Free Software Foundation releases into a copy of
33 * GUILE, as the General Public License permits, the exception does
34 * not apply to the code that you add in this way. To avoid misleading
35 * anyone as to the status of such modified files, you must delete
36 * this exception notice from them.
37 *
38 * If you write modifications of your own for GUILE, it is your choice
39 * whether to permit this exception to apply to your modifications.
82892bed 40 * If you do not wish that, delete this exception notice. */
7bfd3b9e
JB
41\f
42
43#include "coop-threads.h"
44
45/* A counter of the current number of threads */
46size_t scm_thread_count = 0;
47
48/* This is included rather than compiled separately in order
49 to simplify the configuration mechanism. */
50#include "coop.c"
51
52/* A count-down counter used to determine when to switch
53 contexts */
54size_t scm_switch_counter = SCM_THREAD_SWITCH_COUNT;
55
56coop_m scm_critical_section_mutex;
57
58#ifdef __STDC__
6d71500e 59static size_t
7bfd3b9e
JB
60scm_threads_free_thread (SCM t)
61#else
6d71500e 62static size_t
7bfd3b9e
JB
63scm_threads_free_thread (t)
64 SCM t;
65#endif
66{
67 scm_must_free (SCM_THREAD_DATA (t));
68 return sizeof (coop_t);
69}
70
71#ifdef __STDC__
6d71500e 72static size_t
7bfd3b9e
JB
73scm_threads_free_mutex (SCM m)
74#else
6d71500e 75static size_t
7bfd3b9e
JB
76scm_threads_free_mutex (m)
77 SCM m;
78#endif
79{
80 scm_must_free (SCM_MUTEX_DATA (m));
81 return sizeof (coop_m);
82}
83
84#ifdef __STDC__
6d71500e 85static size_t
7bfd3b9e
JB
86scm_threads_free_condvar (SCM c)
87#else
6d71500e 88static size_t
7bfd3b9e
JB
89scm_threads_free_condvar (c)
90 SCM c;
91#endif
92{
93 scm_must_free (SCM_CONDVAR_DATA (c));
94 return sizeof (coop_c);
95}
96
97#ifdef __STDC__
98void
99scm_threads_init (SCM_STACKITEM *i)
100#else
101void
102scm_threads_init (i)
103 SCM_STACKITEM *i;
104#endif
105{
106 coop_init();
107
108 scm_thread_count = 1;
109
110 coop_global_main.sto = i;
111 coop_global_main.base = i;
112 coop_global_curr = &coop_global_main;
113 coop_all_qput (&coop_global_allq, coop_global_curr);
114
115 coop_mutex_init (&scm_critical_section_mutex);
116
117 coop_global_main.data = 0; /* Initialized in init.c */
118}
119
120#ifdef __STDC__
121void
122scm_threads_mark_stacks ()
123#else
124void
125scm_threads_mark_stacks ()
126#endif
127{
128 coop_t *thread;
129
130 for (thread = coop_global_allq.t.all_next;
131 thread != NULL; thread = thread->all_next)
132 {
133 if (thread == coop_global_curr)
134 {
135 /* Active thread */
136 /* stack_len is long rather than sizet in order to guarantee
137 that &stack_len is long aligned */
138#ifdef STACK_GROWS_UP
139 long stack_len = ((SCM_STACKITEM *) (&thread) -
140 (SCM_STACKITEM *) thread->base);
141
142 /* Protect from the C stack. This must be the first marking
143 * done because it provides information about what objects
144 * are "in-use" by the C code. "in-use" objects are those
145 * for which the values from SCM_LENGTH and SCM_CHARS must remain
146 * usable. This requirement is stricter than a liveness
147 * requirement -- in particular, it constrains the implementation
148 * of scm_resizuve.
149 */
150 SCM_FLUSH_REGISTER_WINDOWS;
151 /* This assumes that all registers are saved into the jmp_buf */
152 setjmp (scm_save_regs_gc_mark);
153 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
154 ((scm_sizet) sizeof scm_save_regs_gc_mark
155 / sizeof (SCM_STACKITEM)));
156
157 scm_mark_locations (((size_t) thread->base,
158 (sizet) stack_len));
159#else
160 long stack_len = ((SCM_STACKITEM *) thread->base -
161 (SCM_STACKITEM *) (&thread));
162
163 /* Protect from the C stack. This must be the first marking
164 * done because it provides information about what objects
165 * are "in-use" by the C code. "in-use" objects are those
166 * for which the values from SCM_LENGTH and SCM_CHARS must remain
167 * usable. This requirement is stricter than a liveness
168 * requirement -- in particular, it constrains the implementation
169 * of scm_resizuve.
170 */
171 SCM_FLUSH_REGISTER_WINDOWS;
172 /* This assumes that all registers are saved into the jmp_buf */
173 setjmp (scm_save_regs_gc_mark);
174 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
175 ((scm_sizet) sizeof scm_save_regs_gc_mark
176 / sizeof (SCM_STACKITEM)));
177
178 scm_mark_locations ((SCM_STACKITEM *) &thread,
179 stack_len);
180#endif
181 }
182 else
183 {
184 /* Suspended thread */
185#ifdef STACK_GROWS_UP
186 long stack_len = ((SCM_STACKITEM *) (thread->sp) -
187 (SCM_STACKITEM *) thread->base);
188
189 scm_mark_locations ((size_t)thread->base,
190 (sizet) stack_len);
191#else
192 long stack_len = ((SCM_STACKITEM *) thread->base -
193 (SCM_STACKITEM *) (thread->sp));
194
195 /* Registers are already on the stack. No need to mark. */
196
197 scm_mark_locations ((SCM_STACKITEM *) (size_t)thread->sp,
198 stack_len);
199#endif
200 }
201
202 /* Mark this thread's root */
203 scm_gc_mark (((scm_root_state *) thread->data) -> handle);
204 }
205}
206
df366c26
MD
207/* NOTE: There are TWO mechanisms for starting a thread: The first one
208 is used when spawning a thread from Scheme, while the second one is
209 used from C.
210
211 It might be argued that the first should be implemented in terms of
212 the second. The reason it isn't is that that would require an
213 extra unnecessary malloc (the thread_args structure). By providing
214 one pair of extra functions (c_launch_thread, scm_spawn_thread) the
215 Scheme threads are started more efficiently. */
216
217/* This is the first thread spawning mechanism: threads from Scheme */
218
0a1a92ab
MD
219typedef struct scheme_launch_data {
220 SCM rootcont;
221 SCM body;
222 SCM handler;
223} scheme_launch_data;
224
225extern SCM scm_apply (SCM, SCM, SCM);
226
227static SCM
39752bec 228scheme_body_bootstrip (scheme_launch_data* data)
0a1a92ab
MD
229{
230 /* First save the new root continuation */
231 data->rootcont = scm_root->rootcont;
232 return scm_apply (data->body, SCM_EOL, SCM_EOL);
233}
234
235static SCM
236scheme_handler_bootstrip (scheme_launch_data* data, SCM tag, SCM throw_args)
237{
238 scm_root->rootcont = data->rootcont;
239 return scm_apply (data->handler, scm_cons (tag, throw_args), SCM_EOL);
240}
241
df366c26
MD
242static void
243scheme_launch_thread (void *p)
7bfd3b9e
JB
244{
245 /* The thread object will be GC protected by being a member of the
246 list given as argument to launch_thread. It will be marked
247 during the conservative sweep of the stack. */
0a1a92ab
MD
248 register SCM argl = (SCM) p;
249 SCM thread = SCM_CAR (argl);
250 scheme_launch_data data;
251 data.rootcont = SCM_BOOL_F;
252 data.body = SCM_CADR (argl);
253 data.handler = SCM_CADDR (argl);
254 scm_internal_cwdr ((scm_catch_body_t) scheme_body_bootstrip,
255 &data,
256 (scm_catch_handler_t) scheme_handler_bootstrip,
257 &data,
258 &thread);
7bfd3b9e 259 scm_thread_count--;
0a1a92ab 260 SCM_DEFER_INTS;
7bfd3b9e
JB
261}
262
263#ifdef __STDC__
264SCM
265scm_call_with_new_thread (SCM argl)
266#else
267SCM
268scm_call_with_new_thread (argl)
269 SCM argl;
270#endif
271{
272 SCM thread;
273
274 /* Check arguments. */
275 {
276 register SCM args = argl;
277 SCM thunk, handler;
0824b524
MD
278 SCM_ASSERT (SCM_NIMP (args),
279 scm_makfrom0str (s_call_with_new_thread),
280 SCM_WNA, NULL);
7bfd3b9e
JB
281 thunk = SCM_CAR (args);
282 SCM_ASSERT (SCM_NFALSEP (scm_thunk_p (thunk)),
283 thunk,
284 SCM_ARG1,
285 s_call_with_new_thread);
286 args = SCM_CDR (args);
0824b524
MD
287 SCM_ASSERT (SCM_NIMP (args),
288 scm_makfrom0str (s_call_with_new_thread),
289 SCM_WNA, NULL);
7bfd3b9e
JB
290 handler = SCM_CAR (args);
291 SCM_ASSERT (SCM_NFALSEP (scm_procedure_p (handler)),
292 handler,
293 SCM_ARG2,
294 s_call_with_new_thread);
0824b524
MD
295 SCM_ASSERT (SCM_NULLP (SCM_CDR (args)),
296 scm_makfrom0str (s_call_with_new_thread),
297 SCM_WNA, NULL);
7bfd3b9e
JB
298 }
299
300 /* Make new thread. */
301 {
302 coop_t *t;
303 SCM root, old_winds;
304
305 /* Unwind wind chain. */
306 old_winds = scm_dynwinds;
307 scm_dowinds (SCM_EOL, scm_ilength (scm_root->dynwinds));
308
309 /* Allocate thread locals. */
310 root = scm_make_root (scm_root->handle);
311 /* Make thread. */
312 SCM_NEWCELL (thread);
313 SCM_DEFER_INTS;
314 SCM_SETCAR (thread, scm_tc16_thread);
315 argl = scm_cons (thread, argl);
0a1a92ab
MD
316 /* Note that we couldn't pass a pointer to argl as data since the
317 argl variable may not exist in memory when the thread starts. */
df366c26 318 t = coop_create (scheme_launch_thread, (void *) argl);
7bfd3b9e
JB
319 t->data = SCM_ROOT_STATE (root);
320 SCM_SETCDR (thread, t);
321 scm_thread_count++;
322 /* Note that the following statement also could cause coop_yield.*/
323 SCM_ALLOW_INTS;
324
325 /* We're now ready for the thread to begin. */
326 coop_yield();
327
328 /* Return to old dynamic context. */
329 scm_dowinds (old_winds, - scm_ilength (old_winds));
330 }
331
332 return thread;
333}
334
df366c26
MD
335/* This is the second thread spawning mechanism: threads from C */
336
0a1a92ab
MD
337typedef struct c_launch_data {
338 union {
339 SCM thread;
340 SCM rootcont;
341 } u;
df366c26
MD
342 scm_catch_body_t body;
343 void *body_data;
344 scm_catch_handler_t handler;
345 void *handler_data;
0a1a92ab
MD
346} c_launch_data;
347
348static SCM
39752bec 349c_body_bootstrip (c_launch_data* data)
0a1a92ab
MD
350{
351 /* First save the new root continuation */
352 data->u.rootcont = scm_root->rootcont;
39752bec 353 return (data->body) (data->body_data);
0a1a92ab
MD
354}
355
356static SCM
357c_handler_bootstrip (c_launch_data* data, SCM tag, SCM throw_args)
358{
359 scm_root->rootcont = data->u.rootcont;
360 return (data->handler) (data->handler_data, tag, throw_args);
361}
df366c26
MD
362
363static void
364c_launch_thread (void *p)
365{
0a1a92ab 366 register c_launch_data *data = (c_launch_data *) p;
df366c26 367 /* The thread object will be GC protected by being on this stack */
0a1a92ab 368 SCM thread = data->u.thread;
df366c26
MD
369 /* We must use the address of `thread', otherwise the compiler will
370 optimize it away. This is OK since the longest SCM_STACKITEM
371 also is a long. */
0a1a92ab
MD
372 scm_internal_cwdr ((scm_catch_body_t) c_body_bootstrip,
373 data,
374 (scm_catch_handler_t) c_handler_bootstrip,
375 data,
df366c26
MD
376 &thread);
377 scm_thread_count--;
0a1a92ab 378 scm_must_free ((char *) data);
df366c26
MD
379}
380
381SCM
382scm_spawn_thread (scm_catch_body_t body, void *body_data,
383 scm_catch_handler_t handler, void *handler_data)
384{
385 SCM thread;
386 coop_t *t;
387 SCM root, old_winds;
0a1a92ab
MD
388 c_launch_data *data = (c_launch_data *) scm_must_malloc (sizeof (*data),
389 "scm_spawn_thread");
df366c26
MD
390
391 /* Unwind wind chain. */
392 old_winds = scm_dynwinds;
393 scm_dowinds (SCM_EOL, scm_ilength (scm_root->dynwinds));
394
395 /* Allocate thread locals. */
396 root = scm_make_root (scm_root->handle);
397 /* Make thread. */
398 SCM_NEWCELL (thread);
399 SCM_DEFER_INTS;
400 SCM_SETCAR (thread, scm_tc16_thread);
401
0a1a92ab
MD
402 data->u.thread = thread;
403 data->body = body;
404 data->body_data = body_data;
405 data->handler = handler;
406 data->handler_data = handler_data;
df366c26 407
0a1a92ab 408 t = coop_create (c_launch_thread, (void *) data);
df366c26
MD
409
410 t->data = SCM_ROOT_STATE (root);
411 SCM_SETCDR (thread, t);
412 scm_thread_count++;
413 /* Note that the following statement also could cause coop_yield.*/
414 SCM_ALLOW_INTS;
415
416 /* We're now ready for the thread to begin. */
417 coop_yield();
418
419 /* Return to old dynamic context. */
420 scm_dowinds (old_winds, - scm_ilength (old_winds));
421
422 return thread;
423}
424
7bfd3b9e
JB
425#ifdef __STDC__
426SCM
427scm_join_thread (SCM t)
428#else
429SCM
430scm_join_thread (t)
431 SCM t;
432#endif
433{
434 SCM_ASSERT (SCM_NIMP (t) && SCM_THREADP (t), t, SCM_ARG1, s_join_thread);
435 coop_join (SCM_THREAD_DATA (t));
436 return SCM_BOOL_T;
437}
438
439#ifdef __STDC__
440SCM
441scm_yield ()
442#else
443SCM
444scm_yield ()
445#endif
446{
447 /* Yield early */
448 scm_switch_counter = SCM_THREAD_SWITCH_COUNT;
449 coop_yield();
450
451 return SCM_BOOL_T;
452}
453
454#ifdef __STDC__
455SCM
456scm_single_thread_p ()
457#else
458SCM
459scm_single_thread_p ()
460#endif
461{
462 return (coop_global_runq.tail == &coop_global_runq.t
463 ? SCM_BOOL_T
464 : SCM_BOOL_F);
465}
466
467#ifdef __STDC__
468SCM
469scm_make_mutex ()
470#else
471SCM
472scm_make_mutex ()
473#endif
474{
475 SCM m;
476 coop_m *data = (coop_m *) scm_must_malloc (sizeof (coop_m), "mutex");
477 SCM_NEWCELL (m);
478 SCM_DEFER_INTS;
479 SCM_SETCAR (m, scm_tc16_mutex);
480 SCM_SETCDR (m, data);
481 SCM_ALLOW_INTS;
482 coop_mutex_init (data);
483 return m;
484}
485
486#ifdef __STDC__
487SCM
488scm_lock_mutex (SCM m)
489#else
490SCM
491scm_lock_mutex (m)
492 SCM m;
493#endif
494{
495 SCM_ASSERT (SCM_NIMP (m) && SCM_MUTEXP (m), m, SCM_ARG1, s_lock_mutex);
496 coop_mutex_lock (SCM_MUTEX_DATA (m));
497 return SCM_BOOL_T;
498}
499
500#ifdef __STDC__
501SCM
502scm_unlock_mutex (SCM m)
503#else
504SCM
505scm_unlock_mutex (m)
506 SCM m;
507#endif
508{
509 SCM_ASSERT (SCM_NIMP (m) && SCM_MUTEXP (m), m, SCM_ARG1, s_unlock_mutex);
510 coop_mutex_unlock(SCM_MUTEX_DATA (m));
511
512 /* Yield early */
513 scm_switch_counter = SCM_THREAD_SWITCH_COUNT;
514 coop_yield();
515
516 return SCM_BOOL_T;
517}
518
519#ifdef __STDC__
520SCM
521scm_make_condition_variable ()
522#else
523SCM
524scm_make_condition_variable ()
525#endif
526{
527 SCM c;
528 coop_c *data = (coop_c *) scm_must_malloc (sizeof (coop_c), "condvar");
529 SCM_NEWCELL (c);
530 SCM_DEFER_INTS;
531 SCM_SETCAR (c, scm_tc16_condvar);
532 SCM_SETCDR (c, data);
533 SCM_ALLOW_INTS;
534 coop_condition_variable_init (SCM_CONDVAR_DATA (c));
535 return c;
536}
537
538#ifdef __STDC__
539SCM
540scm_wait_condition_variable (SCM c, SCM m)
541#else
542SCM
543scm_wait_condition_variable (c, m)
544 SCM c;
545 SCM m;
546#endif
547{
548 SCM_ASSERT (SCM_NIMP (c) && SCM_CONDVARP (c),
549 c,
550 SCM_ARG1,
551 s_wait_condition_variable);
552 SCM_ASSERT (SCM_NIMP (m) && SCM_MUTEXP (m),
553 m,
554 SCM_ARG2,
555 s_wait_condition_variable);
c8bf4ecd
MD
556 coop_condition_variable_wait_mutex (SCM_CONDVAR_DATA (c),
557 SCM_MUTEX_DATA (m));
7bfd3b9e
JB
558 return SCM_BOOL_T;
559}
560
561#ifdef __STDC__
562SCM
563scm_signal_condition_variable (SCM c)
564#else
565SCM
566scm_signal_condition_variable (c)
567 SCM c;
568#endif
569{
570 SCM_ASSERT (SCM_NIMP (c) && SCM_CONDVARP (c),
571 c,
572 SCM_ARG1,
573 s_signal_condition_variable);
574 coop_condition_variable_signal (SCM_CONDVAR_DATA (c));
575 return SCM_BOOL_T;
576}