simplify inline function infrastructure
[bpt/guile.git] / libguile / async.c
CommitLineData
cd038da5 1/* Copyright (C) 1995,1996,1997,1998,2000,2001, 2002, 2004, 2006, 2008, 2009, 2010 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
0eb934f1
LC
25#define SCM_BUILDING_DEPRECATED_CODE
26
a0599745
MD
27#include "libguile/_scm.h"
28#include "libguile/eval.h"
29#include "libguile/throw.h"
30#include "libguile/root.h"
31#include "libguile/smob.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 87
e779fef7
AW
88#define ASYNC_GOT_IT(X) (SCM_SMOB_FLAGS (X))
89#define SET_ASYNC_GOT_IT(X, V) (SCM_SET_SMOB_FLAGS ((X), ((V))))
90#define ASYNC_THUNK(X) SCM_SMOB_OBJECT_1 (X)
0f2d19dd 91
0f2d19dd 92
843e4e9d 93SCM_DEFINE (scm_async, "async", 1, 0, 0,
811cf846
MG
94 (SCM thunk),
95 "Create a new async for the procedure @var{thunk}.")
1bbd0b84 96#define FUNC_NAME s_scm_async
0f2d19dd 97{
e94e3f21 98 SCM_RETURN_NEWSMOB (tc16_async, SCM_UNPACK (thunk));
0f2d19dd 99}
1bbd0b84 100#undef FUNC_NAME
0f2d19dd 101
843e4e9d 102SCM_DEFINE (scm_async_mark, "async-mark", 1, 0, 0,
1bbd0b84 103 (SCM a),
811cf846 104 "Mark the async @var{a} for future execution.")
1bbd0b84 105#define FUNC_NAME s_scm_async_mark
0f2d19dd 106{
e94e3f21 107 VALIDATE_ASYNC (1, a);
e94e3f21 108 SET_ASYNC_GOT_IT (a, 1);
0f2d19dd
JB
109 return SCM_UNSPECIFIED;
110}
1bbd0b84 111#undef FUNC_NAME
0f2d19dd 112
843e4e9d 113SCM_DEFINE (scm_run_asyncs, "run-asyncs", 1, 0, 0,
811cf846
MG
114 (SCM list_of_a),
115 "Execute all thunks from the asyncs of the list @var{list_of_a}.")
1bbd0b84 116#define FUNC_NAME s_scm_run_asyncs
0f2d19dd 117{
c96d76b8 118 while (! SCM_NULL_OR_NIL_P (list_of_a))
0f2d19dd
JB
119 {
120 SCM a;
9f0e55a6 121 SCM_VALIDATE_CONS (1, list_of_a);
1bbd0b84 122 a = SCM_CAR (list_of_a);
e94e3f21 123 VALIDATE_ASYNC (SCM_ARG1, a);
e94e3f21 124 if (ASYNC_GOT_IT (a))
0f2d19dd 125 {
e94e3f21 126 SET_ASYNC_GOT_IT (a, 0);
fdc28395 127 scm_call_0 (ASYNC_THUNK (a));
0f2d19dd 128 }
1bbd0b84 129 list_of_a = SCM_CDR (list_of_a);
0f2d19dd
JB
130 }
131 return SCM_BOOL_T;
132}
1bbd0b84 133#undef FUNC_NAME
0f2d19dd
JB
134
135\f
136
9de87eea
MV
137static scm_i_pthread_mutex_t async_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
138
2d3179db 139/* System asyncs. */
0f2d19dd 140
2d3179db
MV
141void
142scm_async_click ()
0f2d19dd 143{
9de87eea
MV
144 scm_i_thread *t = SCM_I_CURRENT_THREAD;
145 SCM asyncs;
146
402858a4 147 /* Reset pending_asyncs even when asyncs are blocked and not really
9de87eea
MV
148 executed since this will avoid future futile calls to this
149 function. When asyncs are unblocked again, this function is
150 invoked even when pending_asyncs is zero.
402858a4 151 */
2d3179db 152
9de87eea
MV
153 scm_i_scm_pthread_mutex_lock (&async_mutex);
154 t->pending_asyncs = 0;
155 if (t->block_asyncs == 0)
2d3179db 156 {
9de87eea
MV
157 asyncs = t->active_asyncs;
158 t->active_asyncs = SCM_EOL;
159 }
160 else
161 asyncs = SCM_EOL;
162 scm_i_pthread_mutex_unlock (&async_mutex);
163
164 while (scm_is_pair (asyncs))
165 {
166 SCM next = SCM_CDR (asyncs);
167 SCM_SETCDR (asyncs, SCM_BOOL_F);
168 scm_call_0 (SCM_CAR (asyncs));
169 asyncs = next;
2d3179db 170 }
0f2d19dd
JB
171}
172
100ae50d
DH
173#if (SCM_ENABLE_DEPRECATED == 1)
174
2d3179db
MV
175SCM_DEFINE (scm_system_async, "system-async", 1, 0, 0,
176 (SCM thunk),
177 "This function is deprecated. You can use @var{thunk} directly\n"
877f06c3 178 "instead of explicitly creating an async object.\n")
2d3179db
MV
179#define FUNC_NAME s_scm_system_async
180{
181 scm_c_issue_deprecation_warning
182 ("'system-async' is deprecated. "
183 "Use the procedure directly with 'system-async-mark'.");
184 return thunk;
185}
186#undef FUNC_NAME
0f2d19dd 187
100ae50d
DH
188#endif /* SCM_ENABLE_DEPRECATED == 1 */
189
2d3179db 190void
9de87eea 191scm_i_queue_async_cell (SCM c, scm_i_thread *t)
2d3179db 192{
9de87eea
MV
193 SCM sleep_object;
194 scm_i_pthread_mutex_t *sleep_mutex;
195 int sleep_fd;
196 SCM p;
197
198 scm_i_scm_pthread_mutex_lock (&async_mutex);
199 p = t->active_asyncs;
402858a4 200 SCM_SETCDR (c, SCM_EOL);
9de87eea
MV
201 if (!scm_is_pair (p))
202 t->active_asyncs = c;
402858a4 203 else
2d3179db 204 {
402858a4 205 SCM pp;
9de87eea 206 while (scm_is_pair (pp = SCM_CDR (p)))
f6b44bd9 207 {
9de87eea
MV
208 if (scm_is_eq (SCM_CAR (p), SCM_CAR (c)))
209 {
210 scm_i_pthread_mutex_unlock (&async_mutex);
211 return;
212 }
402858a4 213 p = pp;
f6b44bd9 214 }
402858a4 215 SCM_SETCDR (p, c);
2d3179db 216 }
9de87eea
MV
217 t->pending_asyncs = 1;
218 sleep_object = t->sleep_object;
219 sleep_mutex = t->sleep_mutex;
220 sleep_fd = t->sleep_fd;
221 scm_i_pthread_mutex_unlock (&async_mutex);
222
223 if (sleep_mutex)
224 {
225 /* By now, the thread T might be out of its sleep already, or
226 might even be in the next, unrelated sleep. Interrupting it
227 anyway does no harm, however.
228
229 The important thing to prevent here is to signal sleep_cond
230 before T waits on it. This can not happen since T has
231 sleep_mutex locked while setting t->sleep_mutex and will only
232 unlock it again while waiting on sleep_cond.
233 */
234 scm_i_scm_pthread_mutex_lock (sleep_mutex);
235 scm_i_pthread_cond_signal (&t->sleep_cond);
236 scm_i_pthread_mutex_unlock (sleep_mutex);
237 }
238
239 if (sleep_fd >= 0)
240 {
241 char dummy = 0;
634aa8de 242
9de87eea
MV
243 /* Likewise, T might already been done with sleeping here, but
244 interrupting it once too often does no harm. T might also
245 not yet have started sleeping, but this is no problem either
246 since the data written to a pipe will not be lost, unlike a
634aa8de
LC
247 condition variable signal. */
248 full_write (sleep_fd, &dummy, 1);
9de87eea
MV
249 }
250
251 /* This is needed to protect sleep_mutex.
252 */
253 scm_remember_upto_here_1 (sleep_object);
254}
255
256int
257scm_i_setup_sleep (scm_i_thread *t,
258 SCM sleep_object, scm_i_pthread_mutex_t *sleep_mutex,
259 int sleep_fd)
260{
261 int pending;
262
263 scm_i_scm_pthread_mutex_lock (&async_mutex);
264 pending = t->pending_asyncs;
265 if (!pending)
266 {
267 t->sleep_object = sleep_object;
268 t->sleep_mutex = sleep_mutex;
269 t->sleep_fd = sleep_fd;
270 }
271 scm_i_pthread_mutex_unlock (&async_mutex);
272 return pending;
273}
274
275void
276scm_i_reset_sleep (scm_i_thread *t)
277{
278 scm_i_scm_pthread_mutex_lock (&async_mutex);
279 t->sleep_object = SCM_BOOL_F;
280 t->sleep_mutex = NULL;
281 t->sleep_fd = -1;
282 scm_i_pthread_mutex_unlock (&async_mutex);
2d3179db 283}
0f2d19dd 284
2d3179db
MV
285SCM_DEFINE (scm_system_async_mark_for_thread, "system-async-mark", 1, 1, 0,
286 (SCM proc, SCM thread),
0a50eeaa
NJ
287 "Mark @var{proc} (a procedure with zero arguments) for future execution\n"
288 "in @var{thread}. If @var{proc} has already been marked for\n"
289 "@var{thread} but has not been executed yet, this call has no effect.\n"
290 "If @var{thread} is omitted, the thread that called\n"
291 "@code{system-async-mark} is used.\n\n"
292 "This procedure is not safe to be called from C signal handlers. Use\n"
293 "@code{scm_sigaction} or @code{scm_sigaction_for_thread} to install\n"
294 "signal handlers.")
2d3179db
MV
295#define FUNC_NAME s_scm_system_async_mark_for_thread
296{
9de87eea
MV
297 /* The current thread might not have a handle yet. This can happen
298 when the GC runs immediately before allocating the handle. At
299 the end of that GC, a system async might be marked. Thus, we can
300 not use scm_current_thread here.
301 */
302
303 scm_i_thread *t;
304
028e573c 305 if (SCM_UNBNDP (thread))
9de87eea 306 t = SCM_I_CURRENT_THREAD;
028e573c 307 else
402858a4
MV
308 {
309 SCM_VALIDATE_THREAD (2, thread);
310 if (scm_c_thread_exited_p (thread))
311 SCM_MISC_ERROR ("thread has already exited", SCM_EOL);
9de87eea 312 t = SCM_I_THREAD_DATA (thread);
402858a4 313 }
9de87eea 314 scm_i_queue_async_cell (scm_cons (proc, SCM_BOOL_F), t);
2d3179db
MV
315 return SCM_UNSPECIFIED;
316}
317#undef FUNC_NAME
9f0e55a6 318
2d3179db
MV
319SCM
320scm_system_async_mark (SCM proc)
321#define FUNC_NAME s_scm_system_async_mark_for_thread
0f2d19dd 322{
2d3179db 323 return scm_system_async_mark_for_thread (proc, SCM_UNDEFINED);
0f2d19dd 324}
1bbd0b84 325#undef FUNC_NAME
0f2d19dd
JB
326
327\f
328
329
2d3179db
MV
330SCM_DEFINE (scm_noop, "noop", 0, 0, 1,
331 (SCM args),
332 "Do nothing. When called without arguments, return @code{#f},\n"
333 "otherwise return the first argument.")
334#define FUNC_NAME s_scm_noop
0f2d19dd 335{
2d3179db
MV
336 SCM_VALIDATE_REST_ARGUMENT (args);
337 return (SCM_NULL_OR_NIL_P (args) ? SCM_BOOL_F : SCM_CAR (args));
0f2d19dd 338}
1bbd0b84 339#undef FUNC_NAME
0f2d19dd 340
0f2d19dd 341
0f2d19dd
JB
342\f
343
100ae50d 344#if (SCM_ENABLE_DEPRECATED == 1)
e292f7aa 345
843e4e9d 346SCM_DEFINE (scm_unmask_signals, "unmask-signals", 0, 0, 0,
811cf846
MG
347 (),
348 "Unmask signals. The returned value is not specified.")
1bbd0b84 349#define FUNC_NAME s_scm_unmask_signals
0f2d19dd 350{
9de87eea
MV
351 scm_i_thread *t = SCM_I_CURRENT_THREAD;
352
e292f7aa
MV
353 scm_c_issue_deprecation_warning
354 ("'unmask-signals' is deprecated. "
355 "Use 'call-with-blocked-asyncs' instead.");
356
9de87eea 357 if (t->block_asyncs == 0)
e292f7aa 358 SCM_MISC_ERROR ("signals already unmasked", SCM_EOL);
9de87eea 359 t->block_asyncs = 0;
402858a4 360 scm_async_click ();
0f2d19dd
JB
361 return SCM_UNSPECIFIED;
362}
1bbd0b84 363#undef FUNC_NAME
0f2d19dd
JB
364
365
843e4e9d 366SCM_DEFINE (scm_mask_signals, "mask-signals", 0, 0, 0,
811cf846
MG
367 (),
368 "Mask signals. The returned value is not specified.")
1bbd0b84 369#define FUNC_NAME s_scm_mask_signals
0f2d19dd 370{
9de87eea
MV
371 scm_i_thread *t = SCM_I_CURRENT_THREAD;
372
e292f7aa
MV
373 scm_c_issue_deprecation_warning
374 ("'mask-signals' is deprecated. Use 'call-with-blocked-asyncs' instead.");
375
9de87eea 376 if (t->block_asyncs > 0)
e292f7aa 377 SCM_MISC_ERROR ("signals already masked", SCM_EOL);
9de87eea 378 t->block_asyncs = 1;
0f2d19dd
JB
379 return SCM_UNSPECIFIED;
380}
1bbd0b84 381#undef FUNC_NAME
0f2d19dd 382
100ae50d 383#endif /* SCM_ENABLE_DEPRECATED == 1 */
e292f7aa
MV
384
385static void
9de87eea 386increase_block (void *data)
e292f7aa 387{
c98ce8f5
AW
388 scm_i_thread *t = data;
389 t->block_asyncs++;
e292f7aa
MV
390}
391
392static void
9de87eea 393decrease_block (void *data)
e292f7aa 394{
c98ce8f5
AW
395 scm_i_thread *t = data;
396 if (--t->block_asyncs == 0)
402858a4 397 scm_async_click ();
e292f7aa
MV
398}
399
c98ce8f5
AW
400void
401scm_dynwind_block_asyncs (void)
402{
403 scm_i_thread *t = SCM_I_CURRENT_THREAD;
404 scm_dynwind_rewind_handler (increase_block, t, SCM_F_WIND_EXPLICITLY);
405 scm_dynwind_unwind_handler (decrease_block, t, SCM_F_WIND_EXPLICITLY);
406}
407
408void
409scm_dynwind_unblock_asyncs (void)
410{
411 scm_i_thread *t = SCM_I_CURRENT_THREAD;
412 if (t->block_asyncs == 0)
413 scm_misc_error ("scm_with_unblocked_asyncs",
414 "asyncs already unblocked", SCM_EOL);
415 scm_dynwind_rewind_handler (decrease_block, t, SCM_F_WIND_EXPLICITLY);
416 scm_dynwind_unwind_handler (increase_block, t, SCM_F_WIND_EXPLICITLY);
417}
418
e292f7aa
MV
419SCM_DEFINE (scm_call_with_blocked_asyncs, "call-with-blocked-asyncs", 1, 0, 0,
420 (SCM proc),
421 "Call @var{proc} with no arguments and block the execution\n"
422 "of system asyncs by one level for the current thread while\n"
423 "it is running. Return the value returned by @var{proc}.\n")
424#define FUNC_NAME s_scm_call_with_blocked_asyncs
425{
c98ce8f5
AW
426 SCM ans;
427
428 scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE);
429 scm_dynwind_block_asyncs ();
430 ans = scm_call_0 (proc);
431 scm_dynwind_end ();
432
433 return ans;
e292f7aa
MV
434}
435#undef FUNC_NAME
436
437void *
438scm_c_call_with_blocked_asyncs (void *(*proc) (void *data), void *data)
439{
c98ce8f5
AW
440 void* ans;
441
442 scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE);
443 scm_dynwind_block_asyncs ();
444 ans = proc (data);
445 scm_dynwind_end ();
446
447 return ans;
e292f7aa
MV
448}
449
450
451SCM_DEFINE (scm_call_with_unblocked_asyncs, "call-with-unblocked-asyncs", 1, 0, 0,
452 (SCM proc),
453 "Call @var{proc} with no arguments and unblock the execution\n"
454 "of system asyncs by one level for the current thread while\n"
455 "it is running. Return the value returned by @var{proc}.\n")
456#define FUNC_NAME s_scm_call_with_unblocked_asyncs
457{
c98ce8f5
AW
458 SCM ans;
459
9de87eea 460 if (SCM_I_CURRENT_THREAD->block_asyncs == 0)
e292f7aa 461 SCM_MISC_ERROR ("asyncs already unblocked", SCM_EOL);
c98ce8f5
AW
462
463 scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE);
464 scm_dynwind_unblock_asyncs ();
465 ans = scm_call_0 (proc);
466 scm_dynwind_end ();
467
468 return ans;
e292f7aa
MV
469}
470#undef FUNC_NAME
471
472void *
473scm_c_call_with_unblocked_asyncs (void *(*proc) (void *data), void *data)
474{
c98ce8f5
AW
475 void* ans;
476
9de87eea 477 if (SCM_I_CURRENT_THREAD->block_asyncs == 0)
e292f7aa
MV
478 scm_misc_error ("scm_c_call_with_unblocked_asyncs",
479 "asyncs already unblocked", SCM_EOL);
e292f7aa 480
c98ce8f5
AW
481 scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE);
482 scm_dynwind_unblock_asyncs ();
483 ans = proc (data);
484 scm_dynwind_end ();
b57a0953 485
c98ce8f5 486 return ans;
b57a0953
MV
487}
488
46935a1f
LC
489\f
490/* These are function variants of the same-named macros (uppercase) for use
491 outside of libguile. This is so that `SCM_I_CURRENT_THREAD', which may
492 reside in TLS, is not accessed from outside of libguile. It thus allows
493 libguile to be built with the "local-dynamic" TLS model. */
494
495void
496scm_critical_section_start (void)
497{
498 SCM_CRITICAL_SECTION_START;
499}
500
501void
502scm_critical_section_end (void)
503{
504 SCM_CRITICAL_SECTION_END;
505}
506
507void
508scm_async_tick (void)
509{
510 SCM_ASYNC_TICK;
511}
b57a0953 512
0f2d19dd
JB
513\f
514
0f2d19dd
JB
515void
516scm_init_async ()
0f2d19dd 517{
73ea78af 518 tc16_async = scm_make_smob_type ("async", 0);
73ea78af 519
a0599745 520#include "libguile/async.x"
0f2d19dd 521}
89e00824
ML
522
523/*
524 Local Variables:
525 c-file-style: "gnu"
526 End:
527*/