summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2016-10-26 22:32:51 +0200
committerAndy Wingo <wingo@pobox.com>2016-10-26 22:50:26 +0200
commitc957ec7ab0f0a028910dc737e12191f7bdc1ca93 (patch)
tree5949cfd1de4a7eecfacb35611430498a53b4c2fe
parentf3bfe29235199e12b961c3fd1fa92666ad031d0d (diff)
Use atomics for async interrupts
* libguile/__scm.h (SCM_TICK): Always define as scm_async_tick(). * libguile/error.c (scm_syserror, scm_syserror_msg): * libguile/fports.c (fport_read, fport_write): * libguile/_scm.h (SCM_SYSCALL): Replace SCM_ASYNC_TICK with scm_async_tick (). (SCM_ASYNC_TICK, SCM_ASYNC_TICK_WITH_CODE) (SCM_ASYNC_TICK_WITH_GUARD_CODE): Remove internal definitions. We inline into vm-engine.c, the only place where it matters. * libguile/async.h: * libguile/async.c (scm_async_tick, scm_i_setup_sleep): (scm_i_reset_sleep, scm_system_async_mark_for_thread): * libguile/threads.h (struct scm_thread_wake_data): * libguile/threads.h (scm_i_thread): * libguile/threads.c (block_self, guilify_self_1, scm_std_select): Rewrite to use sequentially-consistent atomic references. * libguile/atomics-internal.h (scm_atomic_set_pointer): (scm_atomic_ref_pointer): New definitions. * libguile/finalizers.c (queue_finalizer_async): We can allocate, so just use scm_system_async_mark_for_thread instead of the set-cdr! shenanigans. * libguile/scmsigs.c (take_signal): * libguile/gc.c (queue_after_gc_hook): Adapt to new asyncs mechanism. Can't allocate but we're just manipulating the current thread when no other threads are running so we should be good. * libguile/vm-engine.c (VM_HANDLE_INTERRUPTS): Inline the async_tick business.
-rw-r--r--libguile/__scm.h4
-rw-r--r--libguile/_scm.h24
-rw-r--r--libguile/async.c181
-rw-r--r--libguile/async.h1
-rw-r--r--libguile/atomics-internal.h27
-rw-r--r--libguile/error.c4
-rw-r--r--libguile/finalizers.c26
-rw-r--r--libguile/fports.c4
-rw-r--r--libguile/gc.c9
-rw-r--r--libguile/init.c2
-rw-r--r--libguile/scmsigs.c5
-rw-r--r--libguile/threads.c20
-rw-r--r--libguile/threads.h18
-rw-r--r--libguile/vm-engine.c19
14 files changed, 152 insertions, 192 deletions
diff --git a/libguile/__scm.h b/libguile/__scm.h
index 31e395285..1ea4822a6 100644
--- a/libguile/__scm.h
+++ b/libguile/__scm.h
@@ -474,11 +474,7 @@ typedef long SCM_STACKITEM;
#define SCM_STACK_PTR(ptr) ((SCM_STACKITEM *) (void *) (ptr))
-#ifdef BUILDING_LIBGUILE
-#define SCM_TICK SCM_ASYNC_TICK
-#else
#define SCM_TICK scm_async_tick ()
-#endif
diff --git a/libguile/_scm.h b/libguile/_scm.h
index 60ad08295..e482b7e31 100644
--- a/libguile/_scm.h
+++ b/libguile/_scm.h
@@ -100,7 +100,7 @@
errno = 0; \
line; \
if (EVMSERR == errno && (vaxc$errno>>3)==(SS$_CONTROLC>>3)) \
- SCM_ASYNC_TICK; \
+ scm_async_tick (); \
else \
break; \
} \
@@ -119,7 +119,7 @@
line; \
if (errno == EINTR) \
{ \
- SCM_ASYNC_TICK; \
+ scm_async_tick (); \
errno = EINTR; \
} \
} \
@@ -225,26 +225,6 @@ void scm_ia64_longjmp (scm_i_jmp_buf *, int);
-#define SCM_ASYNC_TICK_WITH_GUARD_CODE(thr, pre, post) \
- do \
- { \
- if (SCM_UNLIKELY (thr->pending_asyncs)) \
- { \
- pre; \
- scm_async_tick (); \
- post; \
- } \
- } \
- while (0)
-
-#define SCM_ASYNC_TICK_WITH_CODE(thr, stmt) \
- SCM_ASYNC_TICK_WITH_GUARD_CODE (thr, stmt, (void) 0)
-#define SCM_ASYNC_TICK \
- SCM_ASYNC_TICK_WITH_CODE (SCM_I_CURRENT_THREAD, (void) 0)
-
-
-
-
#if (defined __GNUC__)
# define SCM_NOINLINE __attribute__ ((__noinline__))
#else
diff --git a/libguile/async.c b/libguile/async.c
index 1cf105881..aa4f508b7 100644
--- a/libguile/async.c
+++ b/libguile/async.c
@@ -24,6 +24,7 @@
#endif
#include "libguile/_scm.h"
+#include "libguile/atomics-internal.h"
#include "libguile/eval.h"
#include "libguile/throw.h"
#include "libguile/root.h"
@@ -50,142 +51,51 @@
*
* Each thread has a list of 'activated asyncs', which is a normal
* Scheme list of procedures with zero arguments. When a thread
- * executes a SCM_ASYNC_TICK statement (which is included in SCM_TICK),
- * it will call all procedures on this list.
+ * executes an scm_async_tick (), it will call all procedures on this
+ * list.
*/
-
-
-static scm_i_pthread_mutex_t async_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
-
-/* System asyncs. */
-
void
scm_async_tick (void)
{
scm_i_thread *t = SCM_I_CURRENT_THREAD;
SCM asyncs;
- /* Reset pending_asyncs even when asyncs are blocked and not really
- executed since this will avoid future futile calls to this
- function. When asyncs are unblocked again, this function is
- invoked even when pending_asyncs is zero.
- */
-
- scm_i_scm_pthread_mutex_lock (&async_mutex);
- t->pending_asyncs = 0;
- if (t->block_asyncs == 0)
- {
- asyncs = t->active_asyncs;
- t->active_asyncs = SCM_EOL;
- }
- else
- asyncs = SCM_EOL;
- scm_i_pthread_mutex_unlock (&async_mutex);
+ if (t->block_asyncs)
+ return;
- while (scm_is_pair (asyncs))
+ asyncs = scm_atomic_swap_scm (&t->pending_asyncs, SCM_EOL);
+ while (!scm_is_null (asyncs))
{
- SCM next = SCM_CDR (asyncs);
- SCM_SETCDR (asyncs, SCM_BOOL_F);
- scm_call_0 (SCM_CAR (asyncs));
+ SCM next = scm_cdr (asyncs);
+ scm_call_0 (scm_car (asyncs));
+ scm_set_cdr_x (asyncs, SCM_BOOL_F);
asyncs = next;
}
}
-void
-scm_i_queue_async_cell (SCM c, scm_i_thread *t)
-{
- SCM sleep_object;
- scm_i_pthread_mutex_t *sleep_mutex;
- int sleep_fd;
- SCM p;
-
- scm_i_scm_pthread_mutex_lock (&async_mutex);
- p = t->active_asyncs;
- SCM_SETCDR (c, SCM_EOL);
- if (!scm_is_pair (p))
- t->active_asyncs = c;
- else
- {
- SCM pp;
- while (scm_is_pair (pp = SCM_CDR (p)))
- {
- if (scm_is_eq (SCM_CAR (p), SCM_CAR (c)))
- {
- scm_i_pthread_mutex_unlock (&async_mutex);
- return;
- }
- p = pp;
- }
- SCM_SETCDR (p, c);
- }
- t->pending_asyncs = 1;
- sleep_object = t->sleep_object;
- sleep_mutex = t->sleep_mutex;
- sleep_fd = t->sleep_fd;
- scm_i_pthread_mutex_unlock (&async_mutex);
-
- if (sleep_mutex)
- {
- /* By now, the thread T might be out of its sleep already, or
- might even be in the next, unrelated sleep. Interrupting it
- anyway does no harm, however.
-
- The important thing to prevent here is to signal sleep_cond
- before T waits on it. This can not happen since T has
- sleep_mutex locked while setting t->sleep_mutex and will only
- unlock it again while waiting on sleep_cond.
- */
- scm_i_scm_pthread_mutex_lock (sleep_mutex);
- scm_i_pthread_cond_signal (&t->sleep_cond);
- scm_i_pthread_mutex_unlock (sleep_mutex);
- }
-
- if (sleep_fd >= 0)
- {
- char dummy = 0;
-
- /* Likewise, T might already been done with sleeping here, but
- interrupting it once too often does no harm. T might also
- not yet have started sleeping, but this is no problem either
- since the data written to a pipe will not be lost, unlike a
- condition variable signal. */
- full_write (sleep_fd, &dummy, 1);
- }
-
- /* This is needed to protect sleep_mutex.
- */
- scm_remember_upto_here_1 (sleep_object);
-}
-
int
scm_i_setup_sleep (scm_i_thread *t,
SCM sleep_object, scm_i_pthread_mutex_t *sleep_mutex,
int sleep_fd)
{
- int pending;
+ struct scm_thread_wake_data *wake;
- scm_i_scm_pthread_mutex_lock (&async_mutex);
- pending = t->pending_asyncs;
- if (!pending)
- {
- t->sleep_object = sleep_object;
- t->sleep_mutex = sleep_mutex;
- t->sleep_fd = sleep_fd;
- }
- scm_i_pthread_mutex_unlock (&async_mutex);
- return pending;
+ wake = scm_gc_typed_calloc (struct scm_thread_wake_data);
+ wake->object = sleep_object;
+ wake->mutex = sleep_mutex;
+ wake->fd = sleep_fd;
+
+ scm_atomic_set_pointer ((void **)&t->wake, wake);
+
+ return !scm_is_null (scm_atomic_ref_scm (&t->pending_asyncs));
}
void
scm_i_reset_sleep (scm_i_thread *t)
{
- scm_i_scm_pthread_mutex_lock (&async_mutex);
- t->sleep_object = SCM_BOOL_F;
- t->sleep_mutex = NULL;
- t->sleep_fd = -1;
- scm_i_pthread_mutex_unlock (&async_mutex);
+ scm_atomic_set_pointer ((void **)&t->wake, NULL);
}
SCM_DEFINE (scm_system_async_mark_for_thread, "system-async-mark", 1, 1, 0,
@@ -200,13 +110,9 @@ SCM_DEFINE (scm_system_async_mark_for_thread, "system-async-mark", 1, 1, 0,
"signal handlers.")
#define FUNC_NAME s_scm_system_async_mark_for_thread
{
- /* The current thread might not have a handle yet. This can happen
- when the GC runs immediately before allocating the handle. At
- the end of that GC, a system async might be marked. Thus, we can
- not use scm_current_thread here.
- */
-
scm_i_thread *t;
+ SCM asyncs;
+ struct scm_thread_wake_data *wake;
if (SCM_UNBNDP (thread))
t = SCM_I_CURRENT_THREAD;
@@ -217,7 +123,48 @@ SCM_DEFINE (scm_system_async_mark_for_thread, "system-async-mark", 1, 1, 0,
SCM_MISC_ERROR ("thread has already exited", SCM_EOL);
t = SCM_I_THREAD_DATA (thread);
}
- scm_i_queue_async_cell (scm_cons (proc, SCM_BOOL_F), t);
+
+ asyncs = scm_atomic_ref_scm (&t->pending_asyncs);
+ do
+ if (scm_is_true (scm_c_memq (proc, asyncs)))
+ return SCM_UNSPECIFIED;
+ while (!scm_atomic_compare_and_swap_scm (&t->pending_asyncs, &asyncs,
+ scm_cons (proc, asyncs)));
+
+ /* At this point the async is enqueued. However if the thread is
+ sleeping, we have to wake it up. */
+ if ((wake = scm_atomic_ref_pointer ((void **) &t->wake)))
+ {
+ /* By now, the thread T might be out of its sleep already, or
+ might even be in the next, unrelated sleep. Interrupting it
+ anyway does no harm, however.
+
+ The important thing to prevent here is to signal sleep_cond
+ before T waits on it. This can not happen since T has
+ sleep_mutex locked while setting t->sleep_mutex and will only
+ unlock it again while waiting on sleep_cond.
+ */
+ scm_i_scm_pthread_mutex_lock (wake->mutex);
+ scm_i_pthread_cond_signal (&t->sleep_cond);
+ scm_i_pthread_mutex_unlock (wake->mutex);
+
+ /* This is needed to protect wake->mutex.
+ */
+ scm_remember_upto_here_1 (wake->object);
+
+ if (wake->fd >= 0)
+ {
+ char dummy = 0;
+
+ /* Likewise, T might already been done with sleeping here, but
+ interrupting it once too often does no harm. T might also
+ not yet have started sleeping, but this is no problem
+ either since the data written to a pipe will not be lost,
+ unlike a condition variable signal. */
+ full_write (wake->fd, &dummy, 1);
+ }
+ }
+
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
diff --git a/libguile/async.h b/libguile/async.h
index 1e9760a58..e8b6ee9c2 100644
--- a/libguile/async.h
+++ b/libguile/async.h
@@ -34,7 +34,6 @@ SCM_API void scm_async_tick (void);
SCM_API void scm_switch (void);
SCM_API SCM scm_system_async_mark (SCM a);
SCM_API SCM scm_system_async_mark_for_thread (SCM a, SCM thread);
-SCM_INTERNAL void scm_i_queue_async_cell (SCM cell, scm_i_thread *);
SCM_INTERNAL int scm_i_setup_sleep (scm_i_thread *,
SCM obj, scm_i_pthread_mutex_t *m,
int fd);
diff --git a/libguile/atomics-internal.h b/libguile/atomics-internal.h
index 9d18cbc1a..9074d8cc3 100644
--- a/libguile/atomics-internal.h
+++ b/libguile/atomics-internal.h
@@ -45,6 +45,16 @@ scm_atomic_compare_and_swap_uint32 (uint32_t *loc, uint32_t *expected,
return atomic_compare_exchange_weak (loc, expected, desired);
}
static inline void
+scm_atomic_set_pointer (void **loc, void *val)
+{
+ atomic_store (loc, val);
+}
+static inline void *
+scm_atomic_ref_pointer (void **loc)
+{
+ return atomic_load (loc);
+}
+static inline void
scm_atomic_set_scm (SCM *loc, SCM val)
{
atomic_store (loc, val);
@@ -100,6 +110,23 @@ scm_atomic_compare_and_swap_uint32 (uint32_t *loc, uint32_t *expected,
}
static inline void
+scm_atomic_set_pointer (void **loc, void *val)
+{
+ scm_i_pthread_mutex_lock (&atomics_lock);
+ *loc = val;
+ scm_i_pthread_mutex_unlock (&atomics_lock);
+}
+static inline void *
+scm_atomic_ref_pointer (void **loc)
+{
+ void *ret;
+ scm_i_pthread_mutex_lock (&atomics_lock);
+ ret = *loc;
+ scm_i_pthread_mutex_unlock (&atomics_lock);
+ return ret;
+}
+
+static inline void
scm_atomic_set_scm (SCM *loc, SCM val)
{
scm_i_pthread_mutex_lock (&atomics_lock);
diff --git a/libguile/error.c b/libguile/error.c
index 89345c2b7..ff84f41d8 100644
--- a/libguile/error.c
+++ b/libguile/error.c
@@ -163,7 +163,7 @@ scm_syserror (const char *subr)
*/
#ifdef EINTR
if (scm_to_int (err) == EINTR)
- SCM_ASYNC_TICK;
+ scm_async_tick ();
#endif
scm_error (scm_system_error_key,
@@ -179,7 +179,7 @@ scm_syserror_msg (const char *subr, const char *message, SCM args, int eno)
/* See above note about the EINTR signal handling race. */
#ifdef EINTR
if (eno == EINTR)
- SCM_ASYNC_TICK;
+ scm_async_tick ();
#endif
scm_error (scm_system_error_key,
subr,
diff --git a/libguile/finalizers.c b/libguile/finalizers.c
index 82f292cd2..9b9075830 100644
--- a/libguile/finalizers.c
+++ b/libguile/finalizers.c
@@ -40,6 +40,8 @@ static int automatic_finalization_p = 1;
static size_t finalization_count;
+static SCM run_finalizers_subr;
+
@@ -132,8 +134,6 @@ scm_i_add_finalizer (void *obj, scm_t_finalizer_proc proc, void *data)
-static SCM finalizer_async_cell;
-
static SCM
run_finalizers_async_thunk (void)
{
@@ -150,19 +150,13 @@ static void
queue_finalizer_async (void)
{
scm_i_thread *t = SCM_I_CURRENT_THREAD;
- static scm_i_pthread_mutex_t lock = SCM_I_PTHREAD_MUTEX_INITIALIZER;
- scm_i_pthread_mutex_lock (&lock);
- /* If t is NULL, that could be because we're allocating in
- threads.c:guilify_self_1. In that case, rely on the
+ /* Could be that the current thread is is NULL when we're allocating
+ in threads.c:guilify_self_1. In that case, rely on the
GC_invoke_finalizers call there after the thread spins up. */
- if (t && scm_is_false (SCM_CDR (finalizer_async_cell)))
- {
- SCM_SETCDR (finalizer_async_cell, t->active_asyncs);
- t->active_asyncs = finalizer_async_cell;
- t->pending_asyncs = 1;
- }
- scm_i_pthread_mutex_unlock (&lock);
+ if (!t) return;
+
+ scm_system_async_mark_for_thread (run_finalizers_subr, t->handle);
}
@@ -418,10 +412,8 @@ scm_init_finalizers (void)
{
/* When the async is to run, the cdr of the pair gets set to the
asyncs queue of the current thread. */
- finalizer_async_cell =
- scm_cons (scm_c_make_gsubr ("%run-finalizers", 0, 0, 0,
- run_finalizers_async_thunk),
- SCM_BOOL_F);
+ run_finalizers_subr = scm_c_make_gsubr ("%run-finalizers", 0, 0, 0,
+ run_finalizers_async_thunk);
if (automatic_finalization_p)
GC_set_finalizer_notifier (queue_finalizer_async);
diff --git a/libguile/fports.c b/libguile/fports.c
index 5886f628d..8fa69933d 100644
--- a/libguile/fports.c
+++ b/libguile/fports.c
@@ -595,7 +595,7 @@ fport_read (SCM port, SCM dst, size_t start, size_t count)
{
if (errno == EINTR)
{
- SCM_ASYNC_TICK;
+ scm_async_tick ();
goto retry;
}
if (errno == EWOULDBLOCK || errno == EAGAIN)
@@ -618,7 +618,7 @@ fport_write (SCM port, SCM src, size_t start, size_t count)
{
if (errno == EINTR)
{
- SCM_ASYNC_TICK;
+ scm_async_tick ();
goto retry;
}
if (errno == EWOULDBLOCK || errno == EAGAIN)
diff --git a/libguile/gc.c b/libguile/gc.c
index b75a688aa..1e9f59683 100644
--- a/libguile/gc.c
+++ b/libguile/gc.c
@@ -685,8 +685,8 @@ after_gc_async_thunk (void)
*/
static void *
queue_after_gc_hook (void * hook_data SCM_UNUSED,
- void *fn_data SCM_UNUSED,
- void *data SCM_UNUSED)
+ void *fn_data SCM_UNUSED,
+ void *data SCM_UNUSED)
{
/* If cell access debugging is enabled, the user may choose to perform
* additional garbage collections after an arbitrary number of cell
@@ -721,9 +721,8 @@ queue_after_gc_hook (void * hook_data SCM_UNUSED,
if (scm_is_false (SCM_CDR (after_gc_async_cell)))
{
- SCM_SETCDR (after_gc_async_cell, t->active_asyncs);
- t->active_asyncs = after_gc_async_cell;
- t->pending_asyncs = 1;
+ SCM_SETCDR (after_gc_async_cell, t->pending_asyncs);
+ t->pending_asyncs = after_gc_async_cell;
}
}
diff --git a/libguile/init.c b/libguile/init.c
index 4b95f3612..8b0813a1b 100644
--- a/libguile/init.c
+++ b/libguile/init.c
@@ -344,7 +344,7 @@ invoke_main_func (void *body_data)
* asyncs a chance to run. This must be done after
* the call to scm_restore_signals.
*/
- SCM_ASYNC_TICK;
+ scm_async_tick ();
/* Indicate success by returning non-NULL.
*/
diff --git a/libguile/scmsigs.c b/libguile/scmsigs.c
index 441da3ec7..b030b0413 100644
--- a/libguile/scmsigs.c
+++ b/libguile/scmsigs.c
@@ -228,9 +228,8 @@ take_signal (int signum)
if (scm_is_false (SCM_CDR (cell)))
{
- SCM_SETCDR (cell, t->active_asyncs);
- t->active_asyncs = cell;
- t->pending_asyncs = 1;
+ SCM_SETCDR (cell, t->pending_asyncs);
+ t->pending_asyncs = cell;
}
#ifndef HAVE_SIGACTION
diff --git a/libguile/threads.c b/libguile/threads.c
index 1dece5694..2a315e472 100644
--- a/libguile/threads.c
+++ b/libguile/threads.c
@@ -275,7 +275,7 @@ thread_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
/*** Blocking on queues. */
-/* See also scm_i_queue_async_cell for how such a block is
+/* See also scm_system_async_mark_for_thread for how such a block is
interrputed.
*/
@@ -309,7 +309,10 @@ block_self (SCM queue, SCM sleep_object, scm_i_pthread_mutex_t *mutex,
int err;
if (scm_i_setup_sleep (t, sleep_object, mutex, -1))
- err = EINTR;
+ {
+ scm_i_reset_sleep (t);
+ err = EINTR;
+ }
else
{
t->block_asyncs++;
@@ -415,9 +418,8 @@ guilify_self_1 (struct GC_stack_base *base)
t.dynstack.base = NULL;
t.dynstack.top = NULL;
t.dynstack.limit = NULL;
- t.active_asyncs = SCM_EOL;
+ t.pending_asyncs = SCM_EOL;
t.block_asyncs = 1;
- t.pending_asyncs = 1;
t.critical_section_level = 0;
t.base = base->mem_base;
#ifdef __ia64__
@@ -426,9 +428,7 @@ guilify_self_1 (struct GC_stack_base *base)
t.continuation_root = SCM_EOL;
t.continuation_base = t.base;
scm_i_pthread_cond_init (&t.sleep_cond, NULL);
- t.sleep_mutex = NULL;
- t.sleep_object = SCM_BOOL_F;
- t.sleep_fd = -1;
+ t.wake = NULL;
t.vp = NULL;
if (pipe2 (t.sleep_pipe, O_CLOEXEC) != 0)
@@ -1776,7 +1776,10 @@ scm_std_select (int nfds,
}
while (scm_i_setup_sleep (t, SCM_BOOL_F, NULL, t->sleep_pipe[1]))
- SCM_TICK;
+ {
+ scm_i_reset_sleep (t);
+ SCM_TICK;
+ }
wakeup_fd = t->sleep_pipe[0];
FD_SET (wakeup_fd, readfds);
@@ -1795,7 +1798,6 @@ scm_std_select (int nfds,
res = args.result;
eno = args.errno_value;
- t->sleep_fd = -1;
scm_i_reset_sleep (t);
if (res > 0 && FD_ISSET (wakeup_fd, readfds))
diff --git a/libguile/threads.h b/libguile/threads.h
index a8bb21a4a..241907db2 100644
--- a/libguile/threads.h
+++ b/libguile/threads.h
@@ -47,6 +47,13 @@ SCM_API scm_t_bits scm_tc16_thread;
SCM_API scm_t_bits scm_tc16_mutex;
SCM_API scm_t_bits scm_tc16_condvar;
+struct scm_thread_wake_data
+{
+ SCM object;
+ scm_i_pthread_mutex_t *mutex;
+ int fd;
+};
+
typedef struct scm_i_thread {
struct scm_i_thread *next_thread;
@@ -67,10 +74,9 @@ typedef struct scm_i_thread {
/* Boolean indicating whether the thread is in guile mode. */
int guile_mode;
- SCM sleep_object;
- scm_i_pthread_mutex_t *sleep_mutex;
+ struct scm_thread_wake_data *wake;
scm_i_pthread_cond_t sleep_cond;
- int sleep_fd, sleep_pipe[2];
+ int sleep_pipe[2];
/* Thread-local freelists; see gc-inline.h. */
void **freelists;
@@ -85,12 +91,10 @@ typedef struct scm_i_thread {
/* For system asyncs.
*/
- SCM active_asyncs; /* The thunks to be run at the next
- safe point */
+ SCM pending_asyncs; /* The thunks to be run at the next
+ safe point. Accessed atomically. */
unsigned int block_asyncs; /* Non-zero means that asyncs should
not be run. */
- unsigned int pending_asyncs; /* Non-zero means that asyncs might be pending.
- */
/* The current continuation root and the stack base for it.
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index 852e10d06..4f66b9e7d 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -127,9 +127,24 @@
#define ABORT_CONTINUATION_HOOK() \
RUN_HOOK0 (abort)
-#define VM_HANDLE_INTERRUPTS \
- SCM_ASYNC_TICK_WITH_GUARD_CODE (thread, SYNC_IP (), CACHE_SP ())
+/* TODO: Invoke asyncs without trampolining out to C. That will let us
+ preempt computations via an asynchronous interrupt. */
+#define VM_HANDLE_INTERRUPTS \
+ do \
+ if (SCM_LIKELY (thread->block_asyncs == 0)) \
+ { \
+ SCM asyncs = scm_atomic_ref_scm (&thread->pending_asyncs); \
+ if (SCM_UNLIKELY (!scm_is_null (asyncs))) \
+ { \
+ SYNC_IP (); \
+ scm_async_tick (); \
+ CACHE_SP (); \
+ } \
+ } \
+ while (0)
+
+
/* Virtual Machine