* modules.h. modules.c (scm_current_module_lookup_closure): New
[bpt/guile.git] / libguile / coop-threads.c
CommitLineData
b97206b1 1/* Copyright (C) 1995, 1996, 1997, 1998, 2000 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. */
0c95b57d
GB
41
42/* Software engineering face-lift by Greg J. Badros, 11-Dec-1999,
43 gjb@cs.washington.edu, http://www.cs.washington.edu/homes/gjb */
44
7bfd3b9e
JB
45\f
46
a0599745
MD
47#include "libguile/validate.h"
48#include "libguile/coop-threads.h"
49#include "libguile/root.h"
50#include "libguile/strings.h"
7bfd3b9e
JB
51
52/* A counter of the current number of threads */
53size_t scm_thread_count = 0;
54
55/* This is included rather than compiled separately in order
56 to simplify the configuration mechanism. */
a0599745 57#include "libguile/coop.c"
7bfd3b9e
JB
58
59/* A count-down counter used to determine when to switch
60 contexts */
61size_t scm_switch_counter = SCM_THREAD_SWITCH_COUNT;
62
63coop_m scm_critical_section_mutex;
64
7bfd3b9e
JB
65void
66scm_threads_init (SCM_STACKITEM *i)
7bfd3b9e
JB
67{
68 coop_init();
69
70 scm_thread_count = 1;
71
21aab5c9 72#ifndef GUILE_PTHREAD_COMPAT
7bfd3b9e 73 coop_global_main.sto = i;
21aab5c9 74#endif
7bfd3b9e
JB
75 coop_global_main.base = i;
76 coop_global_curr = &coop_global_main;
77 coop_all_qput (&coop_global_allq, coop_global_curr);
78
79 coop_mutex_init (&scm_critical_section_mutex);
80
81 coop_global_main.data = 0; /* Initialized in init.c */
82}
83
7bfd3b9e 84void
0c95b57d 85scm_threads_mark_stacks (void)
7bfd3b9e
JB
86{
87 coop_t *thread;
88
89 for (thread = coop_global_allq.t.all_next;
90 thread != NULL; thread = thread->all_next)
91 {
92 if (thread == coop_global_curr)
93 {
94 /* Active thread */
95 /* stack_len is long rather than sizet in order to guarantee
96 that &stack_len is long aligned */
97#ifdef STACK_GROWS_UP
98 long stack_len = ((SCM_STACKITEM *) (&thread) -
99 (SCM_STACKITEM *) thread->base);
100
101 /* Protect from the C stack. This must be the first marking
102 * done because it provides information about what objects
103 * are "in-use" by the C code. "in-use" objects are those
a002f1a2
DH
104 * for which the information about length and base address must
105 * remain usable. This requirement is stricter than a liveness
7bfd3b9e
JB
106 * requirement -- in particular, it constrains the implementation
107 * of scm_resizuve.
108 */
109 SCM_FLUSH_REGISTER_WINDOWS;
110 /* This assumes that all registers are saved into the jmp_buf */
111 setjmp (scm_save_regs_gc_mark);
112 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
113 ((scm_sizet) sizeof scm_save_regs_gc_mark
114 / sizeof (SCM_STACKITEM)));
115
116 scm_mark_locations (((size_t) thread->base,
117 (sizet) stack_len));
118#else
119 long stack_len = ((SCM_STACKITEM *) thread->base -
120 (SCM_STACKITEM *) (&thread));
121
122 /* Protect from the C stack. This must be the first marking
123 * done because it provides information about what objects
124 * are "in-use" by the C code. "in-use" objects are those
a002f1a2
DH
125 * for which the information about length and base address must
126 * remain usable. This requirement is stricter than a liveness
7bfd3b9e
JB
127 * requirement -- in particular, it constrains the implementation
128 * of scm_resizuve.
129 */
130 SCM_FLUSH_REGISTER_WINDOWS;
131 /* This assumes that all registers are saved into the jmp_buf */
132 setjmp (scm_save_regs_gc_mark);
133 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
134 ((scm_sizet) sizeof scm_save_regs_gc_mark
135 / sizeof (SCM_STACKITEM)));
136
137 scm_mark_locations ((SCM_STACKITEM *) &thread,
138 stack_len);
139#endif
140 }
141 else
142 {
143 /* Suspended thread */
144#ifdef STACK_GROWS_UP
145 long stack_len = ((SCM_STACKITEM *) (thread->sp) -
146 (SCM_STACKITEM *) thread->base);
147
148 scm_mark_locations ((size_t)thread->base,
149 (sizet) stack_len);
150#else
151 long stack_len = ((SCM_STACKITEM *) thread->base -
152 (SCM_STACKITEM *) (thread->sp));
153
154 /* Registers are already on the stack. No need to mark. */
155
156 scm_mark_locations ((SCM_STACKITEM *) (size_t)thread->sp,
157 stack_len);
158#endif
159 }
160
161 /* Mark this thread's root */
162 scm_gc_mark (((scm_root_state *) thread->data) -> handle);
163 }
164}
165
df366c26
MD
166/* NOTE: There are TWO mechanisms for starting a thread: The first one
167 is used when spawning a thread from Scheme, while the second one is
168 used from C.
169
170 It might be argued that the first should be implemented in terms of
171 the second. The reason it isn't is that that would require an
172 extra unnecessary malloc (the thread_args structure). By providing
173 one pair of extra functions (c_launch_thread, scm_spawn_thread) the
174 Scheme threads are started more efficiently. */
175
176/* This is the first thread spawning mechanism: threads from Scheme */
177
0a1a92ab
MD
178typedef struct scheme_launch_data {
179 SCM rootcont;
180 SCM body;
181 SCM handler;
182} scheme_launch_data;
183
184extern SCM scm_apply (SCM, SCM, SCM);
185
186static SCM
39752bec 187scheme_body_bootstrip (scheme_launch_data* data)
0a1a92ab
MD
188{
189 /* First save the new root continuation */
190 data->rootcont = scm_root->rootcont;
191 return scm_apply (data->body, SCM_EOL, SCM_EOL);
192}
193
194static SCM
195scheme_handler_bootstrip (scheme_launch_data* data, SCM tag, SCM throw_args)
196{
197 scm_root->rootcont = data->rootcont;
198 return scm_apply (data->handler, scm_cons (tag, throw_args), SCM_EOL);
199}
200
df366c26
MD
201static void
202scheme_launch_thread (void *p)
7bfd3b9e
JB
203{
204 /* The thread object will be GC protected by being a member of the
205 list given as argument to launch_thread. It will be marked
206 during the conservative sweep of the stack. */
0a1a92ab
MD
207 register SCM argl = (SCM) p;
208 SCM thread = SCM_CAR (argl);
209 scheme_launch_data data;
210 data.rootcont = SCM_BOOL_F;
211 data.body = SCM_CADR (argl);
212 data.handler = SCM_CADDR (argl);
213 scm_internal_cwdr ((scm_catch_body_t) scheme_body_bootstrip,
214 &data,
215 (scm_catch_handler_t) scheme_handler_bootstrip,
216 &data,
b97206b1 217 (SCM_STACKITEM *) &thread);
0e551780 218 SCM_SET_CELL_WORD_1 (thread, 0);
7bfd3b9e 219 scm_thread_count--;
0a1a92ab 220 SCM_DEFER_INTS;
7bfd3b9e
JB
221}
222
7bfd3b9e
JB
223SCM
224scm_call_with_new_thread (SCM argl)
7bfd3b9e
JB
225{
226 SCM thread;
227
228 /* Check arguments. */
229 {
230 register SCM args = argl;
231 SCM thunk, handler;
0824b524
MD
232 SCM_ASSERT (SCM_NIMP (args),
233 scm_makfrom0str (s_call_with_new_thread),
234 SCM_WNA, NULL);
7bfd3b9e
JB
235 thunk = SCM_CAR (args);
236 SCM_ASSERT (SCM_NFALSEP (scm_thunk_p (thunk)),
237 thunk,
238 SCM_ARG1,
239 s_call_with_new_thread);
240 args = SCM_CDR (args);
0824b524
MD
241 SCM_ASSERT (SCM_NIMP (args),
242 scm_makfrom0str (s_call_with_new_thread),
243 SCM_WNA, NULL);
7bfd3b9e
JB
244 handler = SCM_CAR (args);
245 SCM_ASSERT (SCM_NFALSEP (scm_procedure_p (handler)),
246 handler,
247 SCM_ARG2,
248 s_call_with_new_thread);
0824b524
MD
249 SCM_ASSERT (SCM_NULLP (SCM_CDR (args)),
250 scm_makfrom0str (s_call_with_new_thread),
251 SCM_WNA, NULL);
7bfd3b9e
JB
252 }
253
254 /* Make new thread. */
255 {
256 coop_t *t;
257 SCM root, old_winds;
258
259 /* Unwind wind chain. */
260 old_winds = scm_dynwinds;
261 scm_dowinds (SCM_EOL, scm_ilength (scm_root->dynwinds));
262
263 /* Allocate thread locals. */
264 root = scm_make_root (scm_root->handle);
265 /* Make thread. */
266 SCM_NEWCELL (thread);
267 SCM_DEFER_INTS;
268 SCM_SETCAR (thread, scm_tc16_thread);
269 argl = scm_cons (thread, argl);
0a1a92ab
MD
270 /* Note that we couldn't pass a pointer to argl as data since the
271 argl variable may not exist in memory when the thread starts. */
df366c26 272 t = coop_create (scheme_launch_thread, (void *) argl);
7bfd3b9e 273 t->data = SCM_ROOT_STATE (root);
7a8e7a6c 274 SCM_SET_CELL_WORD_1 (thread, (scm_bits_t) t);
7bfd3b9e
JB
275 scm_thread_count++;
276 /* Note that the following statement also could cause coop_yield.*/
277 SCM_ALLOW_INTS;
278
279 /* We're now ready for the thread to begin. */
280 coop_yield();
281
282 /* Return to old dynamic context. */
283 scm_dowinds (old_winds, - scm_ilength (old_winds));
284 }
285
286 return thread;
287}
288
df366c26
MD
289/* This is the second thread spawning mechanism: threads from C */
290
0a1a92ab
MD
291typedef struct c_launch_data {
292 union {
293 SCM thread;
294 SCM rootcont;
295 } u;
df366c26
MD
296 scm_catch_body_t body;
297 void *body_data;
298 scm_catch_handler_t handler;
299 void *handler_data;
0a1a92ab
MD
300} c_launch_data;
301
302static SCM
39752bec 303c_body_bootstrip (c_launch_data* data)
0a1a92ab
MD
304{
305 /* First save the new root continuation */
306 data->u.rootcont = scm_root->rootcont;
39752bec 307 return (data->body) (data->body_data);
0a1a92ab
MD
308}
309
310static SCM
311c_handler_bootstrip (c_launch_data* data, SCM tag, SCM throw_args)
312{
313 scm_root->rootcont = data->u.rootcont;
314 return (data->handler) (data->handler_data, tag, throw_args);
315}
df366c26
MD
316
317static void
318c_launch_thread (void *p)
319{
0a1a92ab 320 register c_launch_data *data = (c_launch_data *) p;
df366c26 321 /* The thread object will be GC protected by being on this stack */
0a1a92ab 322 SCM thread = data->u.thread;
df366c26
MD
323 /* We must use the address of `thread', otherwise the compiler will
324 optimize it away. This is OK since the longest SCM_STACKITEM
325 also is a long. */
0a1a92ab
MD
326 scm_internal_cwdr ((scm_catch_body_t) c_body_bootstrip,
327 data,
328 (scm_catch_handler_t) c_handler_bootstrip,
329 data,
b97206b1 330 (SCM_STACKITEM *) &thread);
df366c26 331 scm_thread_count--;
0a1a92ab 332 scm_must_free ((char *) data);
df366c26
MD
333}
334
335SCM
336scm_spawn_thread (scm_catch_body_t body, void *body_data,
337 scm_catch_handler_t handler, void *handler_data)
338{
339 SCM thread;
340 coop_t *t;
341 SCM root, old_winds;
0a1a92ab
MD
342 c_launch_data *data = (c_launch_data *) scm_must_malloc (sizeof (*data),
343 "scm_spawn_thread");
df366c26
MD
344
345 /* Unwind wind chain. */
346 old_winds = scm_dynwinds;
347 scm_dowinds (SCM_EOL, scm_ilength (scm_root->dynwinds));
348
349 /* Allocate thread locals. */
350 root = scm_make_root (scm_root->handle);
351 /* Make thread. */
352 SCM_NEWCELL (thread);
353 SCM_DEFER_INTS;
354 SCM_SETCAR (thread, scm_tc16_thread);
355
0a1a92ab
MD
356 data->u.thread = thread;
357 data->body = body;
358 data->body_data = body_data;
359 data->handler = handler;
360 data->handler_data = handler_data;
df366c26 361
0a1a92ab 362 t = coop_create (c_launch_thread, (void *) data);
df366c26
MD
363
364 t->data = SCM_ROOT_STATE (root);
7a8e7a6c 365 SCM_SET_CELL_WORD_1 (thread, (scm_bits_t) t);
df366c26
MD
366 scm_thread_count++;
367 /* Note that the following statement also could cause coop_yield.*/
368 SCM_ALLOW_INTS;
369
370 /* We're now ready for the thread to begin. */
371 coop_yield();
372
373 /* Return to old dynamic context. */
374 scm_dowinds (old_winds, - scm_ilength (old_winds));
375
376 return thread;
377}
378
7bfd3b9e 379SCM
21e8f468 380scm_join_thread (SCM thread)
0c95b57d 381#define FUNC_NAME s_join_thread
7bfd3b9e 382{
21e8f468
DH
383 coop_t *thread_data;
384 SCM_VALIDATE_THREAD (1, thread);
385 /* Dirk:FIXME:: SCM_THREAD_DATA is a handle for a thread. It may be that a
386 * certain thread implementation uses a value of 0 as a valid thread handle.
387 * With the following code, this thread would always be considered finished.
388 */
389 /* Dirk:FIXME:: With preemptive threading, a thread may finish immediately
390 * after SCM_THREAD_DATA is read. Thus, it must be guaranteed that the
391 * handle remains valid until the thread-object is garbage collected, or
392 * a mutex has to be used for reading and modifying SCM_THREAD_DATA.
393 */
394 thread_data = SCM_THREAD_DATA (thread);
395 if (thread_data)
396 /* The thread is still alive */
397 coop_join (thread_data);
7bfd3b9e
JB
398 return SCM_BOOL_T;
399}
0c95b57d 400#undef FUNC_NAME
7bfd3b9e 401
7bfd3b9e 402SCM
0c95b57d 403scm_yield (void)
7bfd3b9e
JB
404{
405 /* Yield early */
406 scm_switch_counter = SCM_THREAD_SWITCH_COUNT;
407 coop_yield();
408
409 return SCM_BOOL_T;
410}
411
7bfd3b9e 412SCM
0c95b57d 413scm_single_thread_p (void)
7bfd3b9e
JB
414{
415 return (coop_global_runq.tail == &coop_global_runq.t
416 ? SCM_BOOL_T
417 : SCM_BOOL_F);
418}
419
7bfd3b9e 420SCM
0c95b57d 421scm_make_mutex (void)
7bfd3b9e
JB
422{
423 SCM m;
424 coop_m *data = (coop_m *) scm_must_malloc (sizeof (coop_m), "mutex");
23a62151 425
7a8e7a6c 426 SCM_NEWSMOB (m, scm_tc16_mutex, (scm_bits_t) data);
7bfd3b9e
JB
427 coop_mutex_init (data);
428 return m;
429}
430
7bfd3b9e
JB
431SCM
432scm_lock_mutex (SCM m)
7bfd3b9e 433{
cabe682c 434 SCM_ASSERT (SCM_MUTEXP (m), m, SCM_ARG1, s_lock_mutex);
7bfd3b9e
JB
435 coop_mutex_lock (SCM_MUTEX_DATA (m));
436 return SCM_BOOL_T;
437}
438
7bfd3b9e
JB
439SCM
440scm_unlock_mutex (SCM m)
7bfd3b9e 441{
0c95b57d 442 SCM_ASSERT (SCM_MUTEXP (m), m, SCM_ARG1, s_unlock_mutex);
7bfd3b9e
JB
443 coop_mutex_unlock(SCM_MUTEX_DATA (m));
444
445 /* Yield early */
446 scm_switch_counter = SCM_THREAD_SWITCH_COUNT;
447 coop_yield();
448
449 return SCM_BOOL_T;
450}
451
7bfd3b9e 452SCM
0c95b57d 453scm_make_condition_variable (void)
7bfd3b9e
JB
454{
455 SCM c;
456 coop_c *data = (coop_c *) scm_must_malloc (sizeof (coop_c), "condvar");
7a8e7a6c 457 SCM_NEWSMOB (c, scm_tc16_condvar, (scm_bits_t) data);
7bfd3b9e
JB
458 coop_condition_variable_init (SCM_CONDVAR_DATA (c));
459 return c;
460}
461
7bfd3b9e
JB
462SCM
463scm_wait_condition_variable (SCM c, SCM m)
7bfd3b9e 464{
0c95b57d 465 SCM_ASSERT (SCM_CONDVARP (c),
7bfd3b9e
JB
466 c,
467 SCM_ARG1,
468 s_wait_condition_variable);
0c95b57d 469 SCM_ASSERT (SCM_MUTEXP (m),
7bfd3b9e
JB
470 m,
471 SCM_ARG2,
472 s_wait_condition_variable);
c8bf4ecd
MD
473 coop_condition_variable_wait_mutex (SCM_CONDVAR_DATA (c),
474 SCM_MUTEX_DATA (m));
7bfd3b9e
JB
475 return SCM_BOOL_T;
476}
477
7bfd3b9e
JB
478SCM
479scm_signal_condition_variable (SCM c)
7bfd3b9e 480{
0c95b57d 481 SCM_ASSERT (SCM_CONDVARP (c),
7bfd3b9e
JB
482 c,
483 SCM_ARG1,
484 s_signal_condition_variable);
485 coop_condition_variable_signal (SCM_CONDVAR_DATA (c));
486 return SCM_BOOL_T;
487}
89e00824
ML
488
489/*
490 Local Variables:
491 c-file-style: "gnu"
492 End:
493*/