1 /* Copyright (C) 1995,1996,1997,1998,2000,2001, 2002 Free Software Foundation, Inc.
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.
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.
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
21 #include "libguile/_scm.h"
22 #include "libguile/validate.h"
23 #include "libguile/coop-threads.h"
24 #include "libguile/root.h"
26 /* A counter of the current number of threads */
27 size_t scm_thread_count
= 0;
29 /* This is included rather than compiled separately in order
30 to simplify the configuration mechanism. */
31 #include "libguile/coop.c"
33 /* A count-down counter used to determine when to switch
35 size_t scm_switch_counter
= SCM_THREAD_SWITCH_COUNT
;
37 coop_m scm_critical_section_mutex
;
39 static SCM all_threads
;
42 scm_threads_init (SCM_STACKITEM
*i
)
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",
53 #ifndef GUILE_PTHREAD_COMPAT
54 coop_global_main
.sto
= i
;
56 coop_global_main
.base
= i
;
57 coop_global_curr
= &coop_global_main
;
58 coop_all_qput (&coop_global_allq
, coop_global_curr
);
60 coop_mutex_init (&scm_critical_section_mutex
);
62 coop_global_main
.data
= 0; /* Initialized in init.c */
64 coop_global_main
.handle
= scm_cell (scm_tc16_thread
,
65 (scm_t_bits
) &coop_global_main
);
67 scm_gc_register_root (&all_threads
);
68 all_threads
= scm_cons (coop_global_main
.handle
, SCM_EOL
);
72 scm_threads_mark_stacks (void)
76 for (thread
= coop_global_allq
.t
.all_next
;
77 thread
!= NULL
; thread
= thread
->all_next
)
79 if (thread
== coop_global_curr
)
82 /* stack_len is long rather than sizet in order to guarantee
83 that &stack_len is long aligned */
84 #if SCM_STACK_GROWS_UP
85 long stack_len
= ((SCM_STACKITEM
*) (&thread
) -
86 (SCM_STACKITEM
*) thread
->base
);
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
91 * for which the information about length and base address must
92 * remain usable. This requirement is stricter than a liveness
93 * requirement -- in particular, it constrains the implementation
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
,
100 ((size_t) sizeof scm_save_regs_gc_mark
101 / sizeof (SCM_STACKITEM
)));
103 scm_mark_locations (((size_t) thread
->base
,
106 long stack_len
= ((SCM_STACKITEM
*) thread
->base
-
107 (SCM_STACKITEM
*) (&thread
));
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
112 * for which the information about length and base address must
113 * remain usable. This requirement is stricter than a liveness
114 * requirement -- in particular, it constrains the implementation
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
,
121 ((size_t) sizeof scm_save_regs_gc_mark
122 / sizeof (SCM_STACKITEM
)));
124 scm_mark_locations ((SCM_STACKITEM
*) &thread
,
130 /* Suspended thread */
131 #if SCM_STACK_GROWS_UP
132 long stack_len
= ((SCM_STACKITEM
*) (thread
->sp
) -
133 (SCM_STACKITEM
*) thread
->base
);
135 scm_mark_locations ((size_t)thread
->base
,
138 long stack_len
= ((SCM_STACKITEM
*) thread
->base
-
139 (SCM_STACKITEM
*) (thread
->sp
));
141 /* Registers are already on the stack. No need to mark. */
143 scm_mark_locations ((SCM_STACKITEM
*) (size_t)thread
->sp
,
148 /* Mark this thread's root */
149 scm_gc_mark (((scm_root_state
*) thread
->data
) -> handle
);
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
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. */
163 /* This is the first thread spawning mechanism: threads from Scheme */
165 typedef struct scheme_launch_data
{
169 } scheme_launch_data
;
172 scheme_body_bootstrip (scheme_launch_data
* data
)
174 /* First save the new root continuation */
175 data
->rootcont
= scm_root
->rootcont
;
176 return scm_call_0 (data
->body
);
180 scheme_handler_bootstrip (scheme_launch_data
* data
, SCM tag
, SCM throw_args
)
182 scm_root
->rootcont
= data
->rootcont
;
183 return scm_apply_1 (data
->handler
, tag
, throw_args
);
187 scheme_launch_thread (void *p
)
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. */
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
);
198 scm_internal_cwdr ((scm_t_catch_body
) scheme_body_bootstrip
,
200 (scm_t_catch_handler
) scheme_handler_bootstrip
,
202 (SCM_STACKITEM
*) &thread
);
203 SCM_SET_CELL_WORD_1 (thread
, 0);
205 all_threads
= scm_delq (thread
, all_threads
);
211 scm_call_with_new_thread (SCM argl
)
212 #define FUNC_NAME s_call_with_new_thread
216 /* Check arguments. */
218 register SCM args
= argl
;
220 if (!SCM_CONSP (args
))
221 SCM_WRONG_NUM_ARGS ();
222 thunk
= SCM_CAR (args
);
223 SCM_ASSERT (SCM_NFALSEP (scm_thunk_p (thunk
)),
226 s_call_with_new_thread
);
227 args
= SCM_CDR (args
);
228 if (!SCM_CONSP (args
))
229 SCM_WRONG_NUM_ARGS ();
230 handler
= SCM_CAR (args
);
231 SCM_ASSERT (SCM_NFALSEP (scm_procedure_p (handler
)),
234 s_call_with_new_thread
);
235 if (!SCM_NULLP (SCM_CDR (args
)))
236 SCM_WRONG_NUM_ARGS ();
239 /* Make new thread. */
244 /* Unwind wind chain. */
245 old_winds
= scm_dynwinds
;
246 scm_dowinds (SCM_EOL
, scm_ilength (scm_root
->dynwinds
));
248 /* Allocate thread locals. */
249 root
= scm_make_root (scm_root
->handle
);
251 thread
= scm_cell (scm_tc16_thread
, 0);
253 argl
= scm_cons (thread
, argl
);
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. */
256 t
= coop_create (scheme_launch_thread
, (void *) argl
);
257 t
->data
= SCM_ROOT_STATE (root
);
259 SCM_SET_CELL_WORD_1 (thread
, (scm_t_bits
) t
);
261 all_threads
= scm_cons (thread
, all_threads
);
262 /* Note that the following statement also could cause coop_yield.*/
265 /* We're now ready for the thread to begin. */
268 /* Return to old dynamic context. */
269 scm_dowinds (old_winds
, - scm_ilength (old_winds
));
277 /* This is the second thread spawning mechanism: threads from C */
279 typedef struct c_launch_data
{
284 scm_t_catch_body body
;
286 scm_t_catch_handler handler
;
291 c_body_bootstrip (c_launch_data
* data
)
293 /* First save the new root continuation */
294 data
->u
.rootcont
= scm_root
->rootcont
;
295 return (data
->body
) (data
->body_data
);
299 c_handler_bootstrip (c_launch_data
* data
, SCM tag
, SCM throw_args
)
301 scm_root
->rootcont
= data
->u
.rootcont
;
302 return (data
->handler
) (data
->handler_data
, tag
, throw_args
);
306 c_launch_thread (void *p
)
308 register c_launch_data
*data
= (c_launch_data
*) p
;
309 /* The thread object will be GC protected by being on this stack */
310 SCM thread
= data
->u
.thread
;
311 /* We must use the address of `thread', otherwise the compiler will
312 optimize it away. This is OK since the longest SCM_STACKITEM
314 scm_internal_cwdr ((scm_t_catch_body
) c_body_bootstrip
,
316 (scm_t_catch_handler
) c_handler_bootstrip
,
318 (SCM_STACKITEM
*) &thread
);
320 free ((char *) data
);
324 scm_spawn_thread (scm_t_catch_body body
, void *body_data
,
325 scm_t_catch_handler handler
, void *handler_data
)
330 c_launch_data
*data
= (c_launch_data
*) scm_malloc (sizeof (*data
));
332 /* Unwind wind chain. */
333 old_winds
= scm_dynwinds
;
334 scm_dowinds (SCM_EOL
, scm_ilength (scm_root
->dynwinds
));
336 /* Allocate thread locals. */
337 root
= scm_make_root (scm_root
->handle
);
339 thread
= scm_cell (scm_tc16_thread
, 0);
342 data
->u
.thread
= thread
;
344 data
->body_data
= body_data
;
345 data
->handler
= handler
;
346 data
->handler_data
= handler_data
;
348 t
= coop_create (c_launch_thread
, (void *) data
);
349 t
->data
= SCM_ROOT_STATE (root
);
351 SCM_SET_CELL_WORD_1 (thread
, (scm_t_bits
) t
);
353 all_threads
= scm_cons (thread
, all_threads
);
354 /* Note that the following statement also could cause coop_yield.*/
357 /* We're now ready for the thread to begin. */
360 /* Return to old dynamic context. */
361 scm_dowinds (old_winds
, - scm_ilength (old_winds
));
367 scm_current_thread (void)
369 return coop_global_curr
->handle
;
373 scm_all_threads (void)
379 scm_i_thread_root (SCM thread
)
381 return (scm_root_state
*)((coop_t
*)SCM_THREAD_DATA (thread
))->data
;
385 scm_join_thread (SCM thread
)
386 #define FUNC_NAME s_join_thread
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.
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.
399 thread_data
= SCM_THREAD_DATA (thread
);
401 /* The thread is still alive */
402 coop_join (thread_data
);
403 /* XXX - return real result. */
409 scm_c_thread_exited_p (SCM thread
)
410 #define FUNC_NAME s_scm_thread_exited_p
412 SCM_VALIDATE_THREAD (1, thread
);
413 return SCM_THREAD_DATA (thread
) != NULL
;
421 scm_switch_counter
= SCM_THREAD_SWITCH_COUNT
;
428 scm_single_thread_p (void)
430 return (coop_global_runq
.tail
== &coop_global_runq
.t
436 scm_make_mutex (void)
438 SCM m
= scm_make_smob (scm_tc16_mutex
);
439 coop_mutex_init (SCM_MUTEX_DATA (m
));
444 scm_lock_mutex (SCM m
)
446 SCM_ASSERT (SCM_MUTEXP (m
), m
, SCM_ARG1
, s_lock_mutex
);
447 coop_mutex_lock (SCM_MUTEX_DATA (m
));
452 scm_try_mutex (SCM m
)
454 SCM_ASSERT (SCM_MUTEXP (m
), m
, SCM_ARG1
, s_lock_mutex
);
455 return SCM_BOOL (coop_mutex_trylock (SCM_MUTEX_DATA (m
)));
459 scm_unlock_mutex (SCM m
)
461 SCM_ASSERT (SCM_MUTEXP (m
), m
, SCM_ARG1
, s_unlock_mutex
);
462 coop_mutex_unlock(SCM_MUTEX_DATA (m
));
465 scm_switch_counter
= SCM_THREAD_SWITCH_COUNT
;
472 scm_make_condition_variable (void)
474 SCM c
= scm_make_smob (scm_tc16_condvar
);
475 coop_condition_variable_init (SCM_CONDVAR_DATA (c
));
480 scm_timed_wait_condition_variable (SCM c
, SCM m
, SCM t
)
481 #define FUNC_NAME s_wait_condition_variable
485 scm_t_timespec waittime
;
487 SCM_ASSERT (SCM_CONDVARP (c
),
490 s_wait_condition_variable
);
491 SCM_ASSERT (SCM_MUTEXP (m
),
494 s_wait_condition_variable
);
496 cv
= SCM_CONDVAR_DATA (c
);
497 mx
= SCM_MUTEX_DATA (m
);
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;
509 SCM_VALIDATE_UINT_COPY (3, t
, waittime
.tv_sec
);
510 waittime
.tv_nsec
= 0;
513 coop_condition_variable_timed_wait_mutex (cv
, mx
, &waittime
));
517 coop_condition_variable_wait_mutex (cv
, mx
);
524 scm_signal_condition_variable (SCM c
)
526 SCM_ASSERT (SCM_CONDVARP (c
),
529 s_signal_condition_variable
);
530 coop_condition_variable_signal (SCM_CONDVAR_DATA (c
));
535 scm_broadcast_condition_variable (SCM c
)
537 SCM_ASSERT (SCM_CONDVARP (c
),
540 s_broadcast_condition_variable
);
541 coop_condition_variable_broadcast (SCM_CONDVAR_DATA (c
));