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