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