Changed license terms to the plain LGPL thru-out.
[bpt/guile.git] / libguile / coop-threads.c
CommitLineData
79cd5b8e 1/* Copyright (C) 1995,1996,1997,1998,2000,2001, 2002 Free Software Foundation, Inc.
7bfd3b9e 2 *
73be1d9e
MV
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7bfd3b9e 7 *
73be1d9e
MV
8 * This library 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 GNU
11 * Lesser General Public License for more details.
7bfd3b9e 12 *
73be1d9e
MV
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
0c95b57d 17
0c95b57d 18
7bfd3b9e
JB
19\f
20
24454006 21#include "libguile/_scm.h"
a0599745
MD
22#include "libguile/validate.h"
23#include "libguile/coop-threads.h"
24#include "libguile/root.h"
7bfd3b9e
JB
25
26/* A counter of the current number of threads */
27size_t scm_thread_count = 0;
28
29/* This is included rather than compiled separately in order
30 to simplify the configuration mechanism. */
a0599745 31#include "libguile/coop.c"
7bfd3b9e
JB
32
33/* A count-down counter used to determine when to switch
34 contexts */
35size_t scm_switch_counter = SCM_THREAD_SWITCH_COUNT;
36
37coop_m scm_critical_section_mutex;
38
9997213b
MV
39static SCM all_threads;
40
7bfd3b9e
JB
41void
42scm_threads_init (SCM_STACKITEM *i)
7bfd3b9e
JB
43{
44 coop_init();
45
79cd5b8e
MV
46 scm_tc16_thread = scm_make_smob_type ("thread", 0);
47 scm_tc16_mutex = scm_make_smob_type ("mutex", sizeof (coop_m));
48 scm_tc16_condvar = scm_make_smob_type ("condition-variable",
49 sizeof (coop_c));
50
7bfd3b9e
JB
51 scm_thread_count = 1;
52
21aab5c9 53#ifndef GUILE_PTHREAD_COMPAT
7bfd3b9e 54 coop_global_main.sto = i;
21aab5c9 55#endif
7bfd3b9e
JB
56 coop_global_main.base = i;
57 coop_global_curr = &coop_global_main;
58 coop_all_qput (&coop_global_allq, coop_global_curr);
59
60 coop_mutex_init (&scm_critical_section_mutex);
61
62 coop_global_main.data = 0; /* Initialized in init.c */
9997213b
MV
63
64 coop_global_main.handle = scm_cell (scm_tc16_thread,
65 (scm_t_bits) &coop_global_main);
66
67 scm_gc_register_root (&all_threads);
68 all_threads = scm_cons (coop_global_main.handle, SCM_EOL);
7bfd3b9e
JB
69}
70
7bfd3b9e 71void
0c95b57d 72scm_threads_mark_stacks (void)
7bfd3b9e
JB
73{
74 coop_t *thread;
75
76 for (thread = coop_global_allq.t.all_next;
77 thread != NULL; thread = thread->all_next)
78 {
79 if (thread == coop_global_curr)
80 {
81 /* Active thread */
82 /* stack_len is long rather than sizet in order to guarantee
83 that &stack_len is long aligned */
16a16ad8 84#if SCM_STACK_GROWS_UP
7bfd3b9e
JB
85 long stack_len = ((SCM_STACKITEM *) (&thread) -
86 (SCM_STACKITEM *) thread->base);
87
88 /* Protect from the C stack. This must be the first marking
89 * done because it provides information about what objects
90 * are "in-use" by the C code. "in-use" objects are those
a002f1a2
DH
91 * for which the information about length and base address must
92 * remain usable. This requirement is stricter than a liveness
7bfd3b9e
JB
93 * requirement -- in particular, it constrains the implementation
94 * of scm_resizuve.
95 */
96 SCM_FLUSH_REGISTER_WINDOWS;
97 /* This assumes that all registers are saved into the jmp_buf */
98 setjmp (scm_save_regs_gc_mark);
99 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
1be6b49c 100 ((size_t) sizeof scm_save_regs_gc_mark
7bfd3b9e
JB
101 / sizeof (SCM_STACKITEM)));
102
103 scm_mark_locations (((size_t) thread->base,
104 (sizet) stack_len));
105#else
106 long stack_len = ((SCM_STACKITEM *) thread->base -
107 (SCM_STACKITEM *) (&thread));
108
109 /* Protect from the C stack. This must be the first marking
110 * done because it provides information about what objects
111 * are "in-use" by the C code. "in-use" objects are those
a002f1a2
DH
112 * for which the information about length and base address must
113 * remain usable. This requirement is stricter than a liveness
7bfd3b9e
JB
114 * requirement -- in particular, it constrains the implementation
115 * of scm_resizuve.
116 */
117 SCM_FLUSH_REGISTER_WINDOWS;
118 /* This assumes that all registers are saved into the jmp_buf */
119 setjmp (scm_save_regs_gc_mark);
120 scm_mark_locations ((SCM_STACKITEM *) scm_save_regs_gc_mark,
1be6b49c 121 ((size_t) sizeof scm_save_regs_gc_mark
7bfd3b9e
JB
122 / sizeof (SCM_STACKITEM)));
123
124 scm_mark_locations ((SCM_STACKITEM *) &thread,
125 stack_len);
126#endif
127 }
128 else
129 {
130 /* Suspended thread */
16a16ad8 131#if SCM_STACK_GROWS_UP
7bfd3b9e
JB
132 long stack_len = ((SCM_STACKITEM *) (thread->sp) -
133 (SCM_STACKITEM *) thread->base);
134
135 scm_mark_locations ((size_t)thread->base,
136 (sizet) stack_len);
137#else
138 long stack_len = ((SCM_STACKITEM *) thread->base -
139 (SCM_STACKITEM *) (thread->sp));
140
141 /* Registers are already on the stack. No need to mark. */
142
143 scm_mark_locations ((SCM_STACKITEM *) (size_t)thread->sp,
144 stack_len);
145#endif
146 }
147
148 /* Mark this thread's root */
149 scm_gc_mark (((scm_root_state *) thread->data) -> handle);
150 }
151}
152
df366c26
MD
153/* NOTE: There are TWO mechanisms for starting a thread: The first one
154 is used when spawning a thread from Scheme, while the second one is
155 used from C.
156
157 It might be argued that the first should be implemented in terms of
158 the second. The reason it isn't is that that would require an
159 extra unnecessary malloc (the thread_args structure). By providing
160 one pair of extra functions (c_launch_thread, scm_spawn_thread) the
161 Scheme threads are started more efficiently. */
162
163/* This is the first thread spawning mechanism: threads from Scheme */
164
0a1a92ab
MD
165typedef struct scheme_launch_data {
166 SCM rootcont;
167 SCM body;
168 SCM handler;
169} scheme_launch_data;
170
0a1a92ab 171static SCM
39752bec 172scheme_body_bootstrip (scheme_launch_data* data)
0a1a92ab
MD
173{
174 /* First save the new root continuation */
175 data->rootcont = scm_root->rootcont;
fdc28395 176 return scm_call_0 (data->body);
0a1a92ab
MD
177}
178
179static SCM
180scheme_handler_bootstrip (scheme_launch_data* data, SCM tag, SCM throw_args)
181{
182 scm_root->rootcont = data->rootcont;
fdc28395 183 return scm_apply_1 (data->handler, tag, throw_args);
0a1a92ab
MD
184}
185
df366c26
MD
186static void
187scheme_launch_thread (void *p)
7bfd3b9e
JB
188{
189 /* The thread object will be GC protected by being a member of the
190 list given as argument to launch_thread. It will be marked
191 during the conservative sweep of the stack. */
0a1a92ab
MD
192 register SCM argl = (SCM) p;
193 SCM thread = SCM_CAR (argl);
194 scheme_launch_data data;
195 data.rootcont = SCM_BOOL_F;
196 data.body = SCM_CADR (argl);
197 data.handler = SCM_CADDR (argl);
92c2555f 198 scm_internal_cwdr ((scm_t_catch_body) scheme_body_bootstrip,
0a1a92ab 199 &data,
92c2555f 200 (scm_t_catch_handler) scheme_handler_bootstrip,
0a1a92ab 201 &data,
b97206b1 202 (SCM_STACKITEM *) &thread);
0e551780 203 SCM_SET_CELL_WORD_1 (thread, 0);
7bfd3b9e 204 scm_thread_count--;
9997213b 205 all_threads = scm_delq (thread, all_threads);
0a1a92ab 206 SCM_DEFER_INTS;
7bfd3b9e
JB
207}
208
2ade72d7 209
7bfd3b9e
JB
210SCM
211scm_call_with_new_thread (SCM argl)
2ade72d7 212#define FUNC_NAME s_call_with_new_thread
7bfd3b9e
JB
213{
214 SCM thread;
215
216 /* Check arguments. */
217 {
218 register SCM args = argl;
219 SCM thunk, handler;
2ade72d7
DH
220 if (!SCM_CONSP (args))
221 SCM_WRONG_NUM_ARGS ();
7bfd3b9e
JB
222 thunk = SCM_CAR (args);
223 SCM_ASSERT (SCM_NFALSEP (scm_thunk_p (thunk)),
224 thunk,
225 SCM_ARG1,
226 s_call_with_new_thread);
227 args = SCM_CDR (args);
2ade72d7
DH
228 if (!SCM_CONSP (args))
229 SCM_WRONG_NUM_ARGS ();
7bfd3b9e
JB
230 handler = SCM_CAR (args);
231 SCM_ASSERT (SCM_NFALSEP (scm_procedure_p (handler)),
232 handler,
233 SCM_ARG2,
234 s_call_with_new_thread);
70ce100d 235 if (!SCM_NULLP (SCM_CDR (args)))
2ade72d7 236 SCM_WRONG_NUM_ARGS ();
7bfd3b9e
JB
237 }
238
239 /* Make new thread. */
240 {
241 coop_t *t;
242 SCM root, old_winds;
243
244 /* Unwind wind chain. */
245 old_winds = scm_dynwinds;
246 scm_dowinds (SCM_EOL, scm_ilength (scm_root->dynwinds));
247
248 /* Allocate thread locals. */
249 root = scm_make_root (scm_root->handle);
250 /* Make thread. */
228a24ef 251 thread = scm_cell (scm_tc16_thread, 0);
7bfd3b9e 252 SCM_DEFER_INTS;
7bfd3b9e 253 argl = scm_cons (thread, argl);
0a1a92ab
MD
254 /* Note that we couldn't pass a pointer to argl as data since the
255 argl variable may not exist in memory when the thread starts. */
df366c26 256 t = coop_create (scheme_launch_thread, (void *) argl);
7bfd3b9e 257 t->data = SCM_ROOT_STATE (root);
9997213b 258 t->handle = thread;
92c2555f 259 SCM_SET_CELL_WORD_1 (thread, (scm_t_bits) t);
7bfd3b9e 260 scm_thread_count++;
9997213b 261 all_threads = scm_cons (thread, all_threads);
7bfd3b9e
JB
262 /* Note that the following statement also could cause coop_yield.*/
263 SCM_ALLOW_INTS;
264
265 /* We're now ready for the thread to begin. */
266 coop_yield();
267
268 /* Return to old dynamic context. */
269 scm_dowinds (old_winds, - scm_ilength (old_winds));
270 }
271
272 return thread;
273}
2ade72d7
DH
274#undef FUNC_NAME
275
7bfd3b9e 276
df366c26
MD
277/* This is the second thread spawning mechanism: threads from C */
278
0a1a92ab
MD
279typedef struct c_launch_data {
280 union {
281 SCM thread;
282 SCM rootcont;
283 } u;
92c2555f 284 scm_t_catch_body body;
df366c26 285 void *body_data;
92c2555f 286 scm_t_catch_handler handler;
df366c26 287 void *handler_data;
0a1a92ab
MD
288} c_launch_data;
289
290static SCM
39752bec 291c_body_bootstrip (c_launch_data* data)
0a1a92ab
MD
292{
293 /* First save the new root continuation */
294 data->u.rootcont = scm_root->rootcont;
39752bec 295 return (data->body) (data->body_data);
0a1a92ab
MD
296}
297
298static SCM
299c_handler_bootstrip (c_launch_data* data, SCM tag, SCM throw_args)
300{
301 scm_root->rootcont = data->u.rootcont;
302 return (data->handler) (data->handler_data, tag, throw_args);
303}
df366c26
MD
304
305static void
306c_launch_thread (void *p)
307{
0a1a92ab 308 register c_launch_data *data = (c_launch_data *) p;
df366c26 309 /* The thread object will be GC protected by being on this stack */
0a1a92ab 310 SCM thread = data->u.thread;
df366c26
MD
311 /* We must use the address of `thread', otherwise the compiler will
312 optimize it away. This is OK since the longest SCM_STACKITEM
313 also is a long. */
92c2555f 314 scm_internal_cwdr ((scm_t_catch_body) c_body_bootstrip,
0a1a92ab 315 data,
92c2555f 316 (scm_t_catch_handler) c_handler_bootstrip,
0a1a92ab 317 data,
b97206b1 318 (SCM_STACKITEM *) &thread);
df366c26 319 scm_thread_count--;
4c9419ac 320 free ((char *) data);
df366c26
MD
321}
322
323SCM
92c2555f
MV
324scm_spawn_thread (scm_t_catch_body body, void *body_data,
325 scm_t_catch_handler handler, void *handler_data)
df366c26
MD
326{
327 SCM thread;
328 coop_t *t;
329 SCM root, old_winds;
4c9419ac 330 c_launch_data *data = (c_launch_data *) scm_malloc (sizeof (*data));
df366c26
MD
331
332 /* Unwind wind chain. */
333 old_winds = scm_dynwinds;
334 scm_dowinds (SCM_EOL, scm_ilength (scm_root->dynwinds));
335
336 /* Allocate thread locals. */
337 root = scm_make_root (scm_root->handle);
338 /* Make thread. */
228a24ef 339 thread = scm_cell (scm_tc16_thread, 0);
df366c26 340 SCM_DEFER_INTS;
df366c26 341
0a1a92ab
MD
342 data->u.thread = thread;
343 data->body = body;
344 data->body_data = body_data;
345 data->handler = handler;
346 data->handler_data = handler_data;
df366c26 347
0a1a92ab 348 t = coop_create (c_launch_thread, (void *) data);
df366c26 349 t->data = SCM_ROOT_STATE (root);
9997213b 350 t->handle = thread;
92c2555f 351 SCM_SET_CELL_WORD_1 (thread, (scm_t_bits) t);
df366c26 352 scm_thread_count++;
9997213b 353 all_threads = scm_cons (thread, all_threads);
df366c26
MD
354 /* Note that the following statement also could cause coop_yield.*/
355 SCM_ALLOW_INTS;
356
357 /* We're now ready for the thread to begin. */
358 coop_yield();
359
360 /* Return to old dynamic context. */
361 scm_dowinds (old_winds, - scm_ilength (old_winds));
362
363 return thread;
364}
365
9997213b
MV
366SCM
367scm_current_thread (void)
368{
369 return coop_global_curr->handle;
370}
371
372SCM
373scm_all_threads (void)
374{
375 return all_threads;
376}
377
378scm_root_state *
379scm_i_thread_root (SCM thread)
9997213b 380{
9997213b
MV
381 return (scm_root_state *)((coop_t *)SCM_THREAD_DATA (thread))->data;
382}
9997213b 383
7bfd3b9e 384SCM
21e8f468 385scm_join_thread (SCM thread)
0c95b57d 386#define FUNC_NAME s_join_thread
7bfd3b9e 387{
21e8f468
DH
388 coop_t *thread_data;
389 SCM_VALIDATE_THREAD (1, thread);
390 /* Dirk:FIXME:: SCM_THREAD_DATA is a handle for a thread. It may be that a
391 * certain thread implementation uses a value of 0 as a valid thread handle.
392 * With the following code, this thread would always be considered finished.
393 */
394 /* Dirk:FIXME:: With preemptive threading, a thread may finish immediately
395 * after SCM_THREAD_DATA is read. Thus, it must be guaranteed that the
396 * handle remains valid until the thread-object is garbage collected, or
397 * a mutex has to be used for reading and modifying SCM_THREAD_DATA.
398 */
399 thread_data = SCM_THREAD_DATA (thread);
400 if (thread_data)
401 /* The thread is still alive */
402 coop_join (thread_data);
79cd5b8e 403 /* XXX - return real result. */
7bfd3b9e
JB
404 return SCM_BOOL_T;
405}
0c95b57d 406#undef FUNC_NAME
7bfd3b9e 407
79cd5b8e
MV
408int
409scm_c_thread_exited_p (SCM thread)
410#define FUNC_NAME s_scm_thread_exited_p
411{
412 SCM_VALIDATE_THREAD (1, thread);
413 return SCM_THREAD_DATA (thread) != NULL;
414}
415#undef FUNC_NAME
416
7bfd3b9e 417SCM
0c95b57d 418scm_yield (void)
7bfd3b9e
JB
419{
420 /* Yield early */
421 scm_switch_counter = SCM_THREAD_SWITCH_COUNT;
422 coop_yield();
423
424 return SCM_BOOL_T;
425}
426
7bfd3b9e 427SCM
0c95b57d 428scm_single_thread_p (void)
7bfd3b9e
JB
429{
430 return (coop_global_runq.tail == &coop_global_runq.t
431 ? SCM_BOOL_T
432 : SCM_BOOL_F);
433}
434
7bfd3b9e 435SCM
0c95b57d 436scm_make_mutex (void)
7bfd3b9e 437{
4c9419ac
MV
438 SCM m = scm_make_smob (scm_tc16_mutex);
439 coop_mutex_init (SCM_MUTEX_DATA (m));
7bfd3b9e
JB
440 return m;
441}
442
7bfd3b9e
JB
443SCM
444scm_lock_mutex (SCM m)
7bfd3b9e 445{
cabe682c 446 SCM_ASSERT (SCM_MUTEXP (m), m, SCM_ARG1, s_lock_mutex);
7bfd3b9e
JB
447 coop_mutex_lock (SCM_MUTEX_DATA (m));
448 return SCM_BOOL_T;
449}
450
79cd5b8e
MV
451SCM
452scm_try_mutex (SCM m)
453{
454 SCM_ASSERT (SCM_MUTEXP (m), m, SCM_ARG1, s_lock_mutex);
455 return SCM_BOOL (coop_mutex_trylock (SCM_MUTEX_DATA (m)));
456}
457
7bfd3b9e
JB
458SCM
459scm_unlock_mutex (SCM m)
7bfd3b9e 460{
0c95b57d 461 SCM_ASSERT (SCM_MUTEXP (m), m, SCM_ARG1, s_unlock_mutex);
7bfd3b9e
JB
462 coop_mutex_unlock(SCM_MUTEX_DATA (m));
463
464 /* Yield early */
465 scm_switch_counter = SCM_THREAD_SWITCH_COUNT;
466 coop_yield();
467
468 return SCM_BOOL_T;
469}
470
7bfd3b9e 471SCM
0c95b57d 472scm_make_condition_variable (void)
7bfd3b9e 473{
4c9419ac 474 SCM c = scm_make_smob (scm_tc16_condvar);
7bfd3b9e
JB
475 coop_condition_variable_init (SCM_CONDVAR_DATA (c));
476 return c;
477}
478
7bfd3b9e 479SCM
79cd5b8e
MV
480scm_timed_wait_condition_variable (SCM c, SCM m, SCM t)
481#define FUNC_NAME s_wait_condition_variable
7bfd3b9e 482{
79cd5b8e
MV
483 coop_c *cv;
484 coop_m *mx;
24454006 485 scm_t_timespec waittime;
79cd5b8e 486
0c95b57d 487 SCM_ASSERT (SCM_CONDVARP (c),
7bfd3b9e
JB
488 c,
489 SCM_ARG1,
490 s_wait_condition_variable);
0c95b57d 491 SCM_ASSERT (SCM_MUTEXP (m),
7bfd3b9e
JB
492 m,
493 SCM_ARG2,
494 s_wait_condition_variable);
79cd5b8e
MV
495
496 cv = SCM_CONDVAR_DATA (c);
497 mx = SCM_MUTEX_DATA (m);
498
499 if (!SCM_UNBNDP (t))
500 {
501 if (SCM_CONSP (t))
502 {
503 SCM_VALIDATE_UINT_COPY (3, SCM_CAR(t), waittime.tv_sec);
504 SCM_VALIDATE_UINT_COPY (3, SCM_CDR(t), waittime.tv_nsec);
505 waittime.tv_nsec *= 1000;
506 }
507 else
508 {
509 SCM_VALIDATE_UINT_COPY (3, t, waittime.tv_sec);
510 waittime.tv_nsec = 0;
511 }
512 return SCM_BOOL(
513 coop_condition_variable_timed_wait_mutex (cv, mx, &waittime));
514 }
515 else
516 {
517 coop_condition_variable_wait_mutex (cv, mx);
518 return SCM_BOOL_T;
519 }
7bfd3b9e 520}
79cd5b8e 521#undef FUNC_NAME
7bfd3b9e 522
7bfd3b9e
JB
523SCM
524scm_signal_condition_variable (SCM c)
7bfd3b9e 525{
0c95b57d 526 SCM_ASSERT (SCM_CONDVARP (c),
7bfd3b9e
JB
527 c,
528 SCM_ARG1,
529 s_signal_condition_variable);
530 coop_condition_variable_signal (SCM_CONDVAR_DATA (c));
531 return SCM_BOOL_T;
532}
89e00824 533
79cd5b8e
MV
534SCM
535scm_broadcast_condition_variable (SCM c)
536{
537 SCM_ASSERT (SCM_CONDVARP (c),
538 c,
539 SCM_ARG1,
540 s_broadcast_condition_variable);
541 coop_condition_variable_broadcast (SCM_CONDVAR_DATA (c));
542 return SCM_BOOL_T;
543}
544
89e00824
ML
545/*
546 Local Variables:
547 c-file-style: "gnu"
548 End:
549*/