Merge commit 'f30e1bdf97ae8b2b2918da585f887a4d3a23a347' into boehm-demers-weiser-gc
[bpt/guile.git] / libguile / async.c
CommitLineData
2b829bbb 1/* Copyright (C) 1995,1996,1997,1998,2000,2001, 2002, 2004, 2006 Free Software Foundation, Inc.
843e4e9d 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.
843e4e9d 7 *
73be1d9e 8 * This library is distributed in the hope that it will be useful,
0f2d19dd 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
843e4e9d 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
92205699 15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
73be1d9e 16 */
1bbd0b84 17
1bbd0b84 18
0f2d19dd 19\f
ef92a2a2
RB
20#if HAVE_CONFIG_H
21# include <config.h>
22#endif
0f2d19dd 23
0f2d19dd 24#include <signal.h>
a0599745
MD
25#include "libguile/_scm.h"
26#include "libguile/eval.h"
27#include "libguile/throw.h"
28#include "libguile/root.h"
29#include "libguile/smob.h"
c96d76b8 30#include "libguile/lang.h"
e292f7aa 31#include "libguile/dynwind.h"
2d3179db 32#include "libguile/deprecation.h"
20e6290e 33
a0599745
MD
34#include "libguile/validate.h"
35#include "libguile/async.h"
0f2d19dd 36
95b88819
GH
37#ifdef HAVE_STRING_H
38#include <string.h>
39#endif
0f2d19dd
JB
40#ifdef HAVE_UNISTD_H
41#include <unistd.h>
42#endif
43
0f2d19dd
JB
44\f
45/* {Asynchronous Events}
46 *
2d3179db
MV
47 * There are two kinds of asyncs: system asyncs and user asyncs. The
48 * two kinds have some concepts in commen but work slightly
49 * differently and are not interchangeable.
0f2d19dd 50 *
2d3179db
MV
51 * System asyncs are used to run arbitrary code at the next safe point
52 * in a specified thread. You can use them to trigger execution of
53 * Scheme code from signal handlers or to interrupt a thread, for
54 * example.
0f2d19dd 55 *
2d3179db
MV
56 * Each thread has a list of 'activated asyncs', which is a normal
57 * Scheme list of procedures with zero arguments. When a thread
58 * executes a SCM_ASYNC_TICK statement (which is included in
59 * SCM_TICK), it will call all procedures on this list.
843e4e9d 60 *
2d3179db
MV
61 * Also, a thread will wake up when a procedure is added to its list
62 * of active asyncs and call them. After that, it will go to sleep
63 * again. (Not implemented yet.)
0f2d19dd 64 *
0f2d19dd 65 *
2d3179db
MV
66 * User asyncs are a little data structure that consists of a
67 * procedure of zero arguments and a mark. There are functions for
68 * setting the mark of a user async and for calling all procedures of
69 * marked asyncs in a given list. Nothing you couldn't quickly
70 * implement yourself.
0f2d19dd
JB
71 */
72
0f2d19dd 73
2d3179db 74\f
0f2d19dd 75
2d3179db 76/* User asyncs. */
e94e3f21 77
2d3179db 78static scm_t_bits tc16_async;
e94e3f21
ML
79
80/* cmm: this has SCM_ prefix because SCM_MAKE_VALIDATE expects it.
81 this is ugly. */
e841c3e0 82#define SCM_ASYNCP(X) SCM_TYP16_PREDICATE (tc16_async, X)
6182ceac 83#define VALIDATE_ASYNC(pos, a) SCM_MAKE_VALIDATE_MSG(pos, a, ASYNCP, "user async")
e94e3f21
ML
84
85#define ASYNC_GOT_IT(X) (SCM_CELL_WORD_0 (X) >> 16)
d1ca2c64 86#define SET_ASYNC_GOT_IT(X, V) (SCM_SET_CELL_WORD_0 ((X), SCM_TYP16 (X) | ((V) << 16)))
e94e3f21 87#define ASYNC_THUNK(X) SCM_CELL_OBJECT_1 (X)
0f2d19dd 88
0f2d19dd 89
843e4e9d 90SCM_DEFINE (scm_async, "async", 1, 0, 0,
811cf846
MG
91 (SCM thunk),
92 "Create a new async for the procedure @var{thunk}.")
1bbd0b84 93#define FUNC_NAME s_scm_async
0f2d19dd 94{
e94e3f21 95 SCM_RETURN_NEWSMOB (tc16_async, SCM_UNPACK (thunk));
0f2d19dd 96}
1bbd0b84 97#undef FUNC_NAME
0f2d19dd 98
843e4e9d 99SCM_DEFINE (scm_async_mark, "async-mark", 1, 0, 0,
1bbd0b84 100 (SCM a),
811cf846 101 "Mark the async @var{a} for future execution.")
1bbd0b84 102#define FUNC_NAME s_scm_async_mark
0f2d19dd 103{
e94e3f21 104 VALIDATE_ASYNC (1, a);
e94e3f21 105 SET_ASYNC_GOT_IT (a, 1);
0f2d19dd
JB
106 return SCM_UNSPECIFIED;
107}
1bbd0b84 108#undef FUNC_NAME
0f2d19dd 109
843e4e9d 110SCM_DEFINE (scm_run_asyncs, "run-asyncs", 1, 0, 0,
811cf846
MG
111 (SCM list_of_a),
112 "Execute all thunks from the asyncs of the list @var{list_of_a}.")
1bbd0b84 113#define FUNC_NAME s_scm_run_asyncs
0f2d19dd 114{
c96d76b8 115 while (! SCM_NULL_OR_NIL_P (list_of_a))
0f2d19dd
JB
116 {
117 SCM a;
9f0e55a6 118 SCM_VALIDATE_CONS (1, list_of_a);
1bbd0b84 119 a = SCM_CAR (list_of_a);
e94e3f21 120 VALIDATE_ASYNC (SCM_ARG1, a);
e94e3f21 121 if (ASYNC_GOT_IT (a))
0f2d19dd 122 {
e94e3f21 123 SET_ASYNC_GOT_IT (a, 0);
fdc28395 124 scm_call_0 (ASYNC_THUNK (a));
0f2d19dd 125 }
1bbd0b84 126 list_of_a = SCM_CDR (list_of_a);
0f2d19dd
JB
127 }
128 return SCM_BOOL_T;
129}
1bbd0b84 130#undef FUNC_NAME
0f2d19dd
JB
131
132\f
133
9de87eea
MV
134static scm_i_pthread_mutex_t async_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
135
2d3179db 136/* System asyncs. */
0f2d19dd 137
2d3179db
MV
138void
139scm_async_click ()
0f2d19dd 140{
9de87eea
MV
141 scm_i_thread *t = SCM_I_CURRENT_THREAD;
142 SCM asyncs;
143
402858a4 144 /* Reset pending_asyncs even when asyncs are blocked and not really
9de87eea
MV
145 executed since this will avoid future futile calls to this
146 function. When asyncs are unblocked again, this function is
147 invoked even when pending_asyncs is zero.
402858a4 148 */
2d3179db 149
9de87eea
MV
150 scm_i_scm_pthread_mutex_lock (&async_mutex);
151 t->pending_asyncs = 0;
152 if (t->block_asyncs == 0)
2d3179db 153 {
9de87eea
MV
154 asyncs = t->active_asyncs;
155 t->active_asyncs = SCM_EOL;
156 }
157 else
158 asyncs = SCM_EOL;
159 scm_i_pthread_mutex_unlock (&async_mutex);
160
161 while (scm_is_pair (asyncs))
162 {
163 SCM next = SCM_CDR (asyncs);
164 SCM_SETCDR (asyncs, SCM_BOOL_F);
165 scm_call_0 (SCM_CAR (asyncs));
166 asyncs = next;
2d3179db 167 }
0f2d19dd
JB
168}
169
100ae50d
DH
170#if (SCM_ENABLE_DEPRECATED == 1)
171
2d3179db
MV
172SCM_DEFINE (scm_system_async, "system-async", 1, 0, 0,
173 (SCM thunk),
174 "This function is deprecated. You can use @var{thunk} directly\n"
100ae50d 175 "instead of explicitely creating an async object.\n")
2d3179db
MV
176#define FUNC_NAME s_scm_system_async
177{
178 scm_c_issue_deprecation_warning
179 ("'system-async' is deprecated. "
180 "Use the procedure directly with 'system-async-mark'.");
181 return thunk;
182}
183#undef FUNC_NAME
0f2d19dd 184
100ae50d
DH
185#endif /* SCM_ENABLE_DEPRECATED == 1 */
186
2d3179db 187void
9de87eea 188scm_i_queue_async_cell (SCM c, scm_i_thread *t)
2d3179db 189{
9de87eea
MV
190 SCM sleep_object;
191 scm_i_pthread_mutex_t *sleep_mutex;
192 int sleep_fd;
193 SCM p;
194
195 scm_i_scm_pthread_mutex_lock (&async_mutex);
196 p = t->active_asyncs;
402858a4 197 SCM_SETCDR (c, SCM_EOL);
9de87eea
MV
198 if (!scm_is_pair (p))
199 t->active_asyncs = c;
402858a4 200 else
2d3179db 201 {
402858a4 202 SCM pp;
9de87eea 203 while (scm_is_pair (pp = SCM_CDR (p)))
f6b44bd9 204 {
9de87eea
MV
205 if (scm_is_eq (SCM_CAR (p), SCM_CAR (c)))
206 {
207 scm_i_pthread_mutex_unlock (&async_mutex);
208 return;
209 }
402858a4 210 p = pp;
f6b44bd9 211 }
402858a4 212 SCM_SETCDR (p, c);
2d3179db 213 }
9de87eea
MV
214 t->pending_asyncs = 1;
215 sleep_object = t->sleep_object;
216 sleep_mutex = t->sleep_mutex;
217 sleep_fd = t->sleep_fd;
218 scm_i_pthread_mutex_unlock (&async_mutex);
219
220 if (sleep_mutex)
221 {
222 /* By now, the thread T might be out of its sleep already, or
223 might even be in the next, unrelated sleep. Interrupting it
224 anyway does no harm, however.
225
226 The important thing to prevent here is to signal sleep_cond
227 before T waits on it. This can not happen since T has
228 sleep_mutex locked while setting t->sleep_mutex and will only
229 unlock it again while waiting on sleep_cond.
230 */
231 scm_i_scm_pthread_mutex_lock (sleep_mutex);
232 scm_i_pthread_cond_signal (&t->sleep_cond);
233 scm_i_pthread_mutex_unlock (sleep_mutex);
234 }
235
236 if (sleep_fd >= 0)
237 {
238 char dummy = 0;
239 /* Likewise, T might already been done with sleeping here, but
240 interrupting it once too often does no harm. T might also
241 not yet have started sleeping, but this is no problem either
242 since the data written to a pipe will not be lost, unlike a
243 condition variable signal.
244 */
245 write (sleep_fd, &dummy, 1);
246 }
247
248 /* This is needed to protect sleep_mutex.
249 */
250 scm_remember_upto_here_1 (sleep_object);
251}
252
253int
254scm_i_setup_sleep (scm_i_thread *t,
255 SCM sleep_object, scm_i_pthread_mutex_t *sleep_mutex,
256 int sleep_fd)
257{
258 int pending;
259
260 scm_i_scm_pthread_mutex_lock (&async_mutex);
261 pending = t->pending_asyncs;
262 if (!pending)
263 {
264 t->sleep_object = sleep_object;
265 t->sleep_mutex = sleep_mutex;
266 t->sleep_fd = sleep_fd;
267 }
268 scm_i_pthread_mutex_unlock (&async_mutex);
269 return pending;
270}
271
272void
273scm_i_reset_sleep (scm_i_thread *t)
274{
275 scm_i_scm_pthread_mutex_lock (&async_mutex);
276 t->sleep_object = SCM_BOOL_F;
277 t->sleep_mutex = NULL;
278 t->sleep_fd = -1;
279 scm_i_pthread_mutex_unlock (&async_mutex);
2d3179db 280}
0f2d19dd 281
2d3179db
MV
282SCM_DEFINE (scm_system_async_mark_for_thread, "system-async-mark", 1, 1, 0,
283 (SCM proc, SCM thread),
0a50eeaa
NJ
284 "Mark @var{proc} (a procedure with zero arguments) for future execution\n"
285 "in @var{thread}. If @var{proc} has already been marked for\n"
286 "@var{thread} but has not been executed yet, this call has no effect.\n"
287 "If @var{thread} is omitted, the thread that called\n"
288 "@code{system-async-mark} is used.\n\n"
289 "This procedure is not safe to be called from C signal handlers. Use\n"
290 "@code{scm_sigaction} or @code{scm_sigaction_for_thread} to install\n"
291 "signal handlers.")
2d3179db
MV
292#define FUNC_NAME s_scm_system_async_mark_for_thread
293{
9de87eea
MV
294 /* The current thread might not have a handle yet. This can happen
295 when the GC runs immediately before allocating the handle. At
296 the end of that GC, a system async might be marked. Thus, we can
297 not use scm_current_thread here.
298 */
299
300 scm_i_thread *t;
301
028e573c 302 if (SCM_UNBNDP (thread))
9de87eea 303 t = SCM_I_CURRENT_THREAD;
028e573c 304 else
402858a4
MV
305 {
306 SCM_VALIDATE_THREAD (2, thread);
307 if (scm_c_thread_exited_p (thread))
308 SCM_MISC_ERROR ("thread has already exited", SCM_EOL);
9de87eea 309 t = SCM_I_THREAD_DATA (thread);
402858a4 310 }
9de87eea 311 scm_i_queue_async_cell (scm_cons (proc, SCM_BOOL_F), t);
2d3179db
MV
312 return SCM_UNSPECIFIED;
313}
314#undef FUNC_NAME
9f0e55a6 315
2d3179db
MV
316SCM
317scm_system_async_mark (SCM proc)
318#define FUNC_NAME s_scm_system_async_mark_for_thread
0f2d19dd 319{
2d3179db 320 return scm_system_async_mark_for_thread (proc, SCM_UNDEFINED);
0f2d19dd 321}
1bbd0b84 322#undef FUNC_NAME
0f2d19dd
JB
323
324\f
325
326
2d3179db
MV
327SCM_DEFINE (scm_noop, "noop", 0, 0, 1,
328 (SCM args),
329 "Do nothing. When called without arguments, return @code{#f},\n"
330 "otherwise return the first argument.")
331#define FUNC_NAME s_scm_noop
0f2d19dd 332{
2d3179db
MV
333 SCM_VALIDATE_REST_ARGUMENT (args);
334 return (SCM_NULL_OR_NIL_P (args) ? SCM_BOOL_F : SCM_CAR (args));
0f2d19dd 335}
1bbd0b84 336#undef FUNC_NAME
0f2d19dd 337
0f2d19dd 338
0f2d19dd
JB
339\f
340
100ae50d 341#if (SCM_ENABLE_DEPRECATED == 1)
e292f7aa 342
843e4e9d 343SCM_DEFINE (scm_unmask_signals, "unmask-signals", 0, 0, 0,
811cf846
MG
344 (),
345 "Unmask signals. The returned value is not specified.")
1bbd0b84 346#define FUNC_NAME s_scm_unmask_signals
0f2d19dd 347{
9de87eea
MV
348 scm_i_thread *t = SCM_I_CURRENT_THREAD;
349
e292f7aa
MV
350 scm_c_issue_deprecation_warning
351 ("'unmask-signals' is deprecated. "
352 "Use 'call-with-blocked-asyncs' instead.");
353
9de87eea 354 if (t->block_asyncs == 0)
e292f7aa 355 SCM_MISC_ERROR ("signals already unmasked", SCM_EOL);
9de87eea 356 t->block_asyncs = 0;
402858a4 357 scm_async_click ();
0f2d19dd
JB
358 return SCM_UNSPECIFIED;
359}
1bbd0b84 360#undef FUNC_NAME
0f2d19dd
JB
361
362
843e4e9d 363SCM_DEFINE (scm_mask_signals, "mask-signals", 0, 0, 0,
811cf846
MG
364 (),
365 "Mask signals. The returned value is not specified.")
1bbd0b84 366#define FUNC_NAME s_scm_mask_signals
0f2d19dd 367{
9de87eea
MV
368 scm_i_thread *t = SCM_I_CURRENT_THREAD;
369
e292f7aa
MV
370 scm_c_issue_deprecation_warning
371 ("'mask-signals' is deprecated. Use 'call-with-blocked-asyncs' instead.");
372
9de87eea 373 if (t->block_asyncs > 0)
e292f7aa 374 SCM_MISC_ERROR ("signals already masked", SCM_EOL);
9de87eea 375 t->block_asyncs = 1;
0f2d19dd
JB
376 return SCM_UNSPECIFIED;
377}
1bbd0b84 378#undef FUNC_NAME
0f2d19dd 379
100ae50d 380#endif /* SCM_ENABLE_DEPRECATED == 1 */
e292f7aa
MV
381
382static void
9de87eea 383increase_block (void *data)
e292f7aa 384{
9de87eea 385 ((scm_i_thread *)data)->block_asyncs++;
e292f7aa
MV
386}
387
388static void
9de87eea 389decrease_block (void *data)
e292f7aa 390{
9de87eea 391 if (--((scm_i_thread *)data)->block_asyncs == 0)
402858a4 392 scm_async_click ();
e292f7aa
MV
393}
394
395SCM_DEFINE (scm_call_with_blocked_asyncs, "call-with-blocked-asyncs", 1, 0, 0,
396 (SCM proc),
397 "Call @var{proc} with no arguments and block the execution\n"
398 "of system asyncs by one level for the current thread while\n"
399 "it is running. Return the value returned by @var{proc}.\n")
400#define FUNC_NAME s_scm_call_with_blocked_asyncs
401{
402 return scm_internal_dynamic_wind (increase_block,
403 (scm_t_inner) scm_call_0,
404 decrease_block,
9de87eea
MV
405 (void *)proc,
406 SCM_I_CURRENT_THREAD);
e292f7aa
MV
407}
408#undef FUNC_NAME
409
410void *
411scm_c_call_with_blocked_asyncs (void *(*proc) (void *data), void *data)
412{
402858a4
MV
413 return (void *)scm_internal_dynamic_wind (increase_block,
414 (scm_t_inner) proc,
415 decrease_block,
9de87eea
MV
416 data,
417 SCM_I_CURRENT_THREAD);
e292f7aa
MV
418}
419
420
421SCM_DEFINE (scm_call_with_unblocked_asyncs, "call-with-unblocked-asyncs", 1, 0, 0,
422 (SCM proc),
423 "Call @var{proc} with no arguments and unblock the execution\n"
424 "of system asyncs by one level for the current thread while\n"
425 "it is running. Return the value returned by @var{proc}.\n")
426#define FUNC_NAME s_scm_call_with_unblocked_asyncs
427{
9de87eea 428 if (SCM_I_CURRENT_THREAD->block_asyncs == 0)
e292f7aa
MV
429 SCM_MISC_ERROR ("asyncs already unblocked", SCM_EOL);
430 return scm_internal_dynamic_wind (decrease_block,
431 (scm_t_inner) scm_call_0,
432 increase_block,
9de87eea
MV
433 (void *)proc,
434 SCM_I_CURRENT_THREAD);
e292f7aa
MV
435}
436#undef FUNC_NAME
437
438void *
439scm_c_call_with_unblocked_asyncs (void *(*proc) (void *data), void *data)
440{
9de87eea 441 if (SCM_I_CURRENT_THREAD->block_asyncs == 0)
e292f7aa
MV
442 scm_misc_error ("scm_c_call_with_unblocked_asyncs",
443 "asyncs already unblocked", SCM_EOL);
402858a4
MV
444 return (void *)scm_internal_dynamic_wind (decrease_block,
445 (scm_t_inner) proc,
446 increase_block,
9de87eea
MV
447 data,
448 SCM_I_CURRENT_THREAD);
e292f7aa
MV
449}
450
b57a0953 451void
661ae7ab 452scm_dynwind_block_asyncs ()
b57a0953 453{
9de87eea 454 scm_i_thread *t = SCM_I_CURRENT_THREAD;
661ae7ab
MV
455 scm_dynwind_rewind_handler (increase_block, t, SCM_F_WIND_EXPLICITLY);
456 scm_dynwind_unwind_handler (decrease_block, t, SCM_F_WIND_EXPLICITLY);
b57a0953
MV
457}
458
459void
661ae7ab 460scm_dynwind_unblock_asyncs ()
b57a0953 461{
9de87eea
MV
462 scm_i_thread *t = SCM_I_CURRENT_THREAD;
463 if (t->block_asyncs == 0)
b57a0953
MV
464 scm_misc_error ("scm_with_unblocked_asyncs",
465 "asyncs already unblocked", SCM_EOL);
661ae7ab
MV
466 scm_dynwind_rewind_handler (decrease_block, t, SCM_F_WIND_EXPLICITLY);
467 scm_dynwind_unwind_handler (increase_block, t, SCM_F_WIND_EXPLICITLY);
b57a0953
MV
468}
469
470
0f2d19dd
JB
471\f
472
0f2d19dd
JB
473void
474scm_init_async ()
0f2d19dd 475{
939794ce 476 scm_asyncs = SCM_EOL;
73ea78af 477 tc16_async = scm_make_smob_type ("async", 0);
73ea78af 478
a0599745 479#include "libguile/async.x"
0f2d19dd 480}
89e00824
ML
481
482/*
483 Local Variables:
484 c-file-style: "gnu"
485 End:
486*/