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