summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarius Vollmer <mvo@zagadka.de>2002-10-27 20:25:17 +0000
committerMarius Vollmer <mvo@zagadka.de>2002-10-27 20:25:17 +0000
commitdbbaa07cabdb8b5225ed404497cdb9827e296917 (patch)
treebe1f6aef08a601d8fa40eedfc117a32a349a0707
parent402858a4d36c70053d3051eaa96e42442affedd8 (diff)
downloadguile-dbbaa07cabdb8b5225ed404497cdb9827e296917.tar.gz
(signal_cell_handlers, install_handler_data,
scm_delq_spine_x, really_install_handler, install_handler): New scheme for triggering signal handlers, to simplify take_signal. (take_signal): Simplified, to avoid race conditions. (scm_sigaction_for_thread): Use new Scheme. Validate that thread hasn't exited yet.
-rw-r--r--libguile/scmsigs.c173
1 files changed, 153 insertions, 20 deletions
diff --git a/libguile/scmsigs.c b/libguile/scmsigs.c
index 4e617794d..b0c0e6548 100644
--- a/libguile/scmsigs.c
+++ b/libguile/scmsigs.c
@@ -108,12 +108,15 @@ int usleep ();
/* Scheme vectors with information about a signal. signal_handlers
contains the handler procedure or #f, signal_handler_cells contains
- preallocated cells for queuing the handler in take_signal since we
- can't allocate during signal delivery, signal_handler_threads
- points to the thread that a signal should be delivered to.
+ pre-queued cells for the handler (since we can't do fancy things
+ during signal delivery), signal_cell_handlers contains the SCM
+ value to be stuffed into the pre-queued cell upon delivery, and
+ signal_handler_threads points to the thread that a signal should be
+ delivered to.
*/
static SCM *signal_handlers;
static SCM signal_handler_cells;
+static SCM signal_cell_handlers;
static SCM signal_handler_threads;
/* saves the original C handlers, when a new handler is installed.
@@ -124,20 +127,23 @@ static struct sigaction orig_handlers[NSIG];
static SIGRETTYPE (*orig_handlers[NSIG])(int);
#endif
+
static SIGRETTYPE
take_signal (int signum)
{
if (signum >= 0 && signum < NSIG)
{
-#ifdef USE_THREADS
- SCM thread = SCM_VECTOR_REF (signal_handler_threads, signum);
- scm_i_queue_async_cell (SCM_VECTOR_REF(signal_handler_cells, signum),
- scm_i_thread_root (thread));
-#else
- scm_i_queue_async_cell (SCM_VECTOR_REF(signal_handler_cells, signum),
- scm_root);
-#endif
+ SCM cell = SCM_VECTOR_REF(signal_handler_cells, signum);
+ SCM handler = SCM_VECTOR_REF(signal_cell_handlers, signum);
+ SCM thread = SCM_VECTOR_REF(signal_handler_threads, signum);
+ scm_root_state *root = scm_i_thread_root (thread);
+ if (SCM_CONSP (cell))
+ {
+ SCM_SETCAR (cell, handler);
+ root->pending_asyncs = 1;
+ }
}
+
#ifndef HAVE_SIGACTION
signal (signum, take_signal);
#endif
@@ -156,6 +162,131 @@ close_1 (SCM proc, SCM arg)
scm_list_2 (proc, arg)));
}
+/* Make sure that signal SIGNUM can be delivered to THREAD, using
+ HANDLER. THREAD and HANDLER must either both be non-#f (which
+ means install the handler), or both #f (which means deinstall an
+ existing handler).
+*/
+
+struct install_handler_data {
+ int signum;
+ SCM thread;
+ SCM handler;
+};
+
+static SCM
+scm_delq_spine_x (SCM cell, SCM list)
+{
+ SCM s = list, prev = SCM_BOOL_F;
+
+ while (!SCM_EQ_P (cell, s))
+ {
+ if (SCM_NULLP (s))
+ return list;
+ prev = s;
+ s = SCM_CDR (s);
+ }
+ if (SCM_FALSEP (prev))
+ return SCM_CDR (cell);
+ else
+ {
+ SCM_SETCDR (prev, SCM_CDR (cell));
+ return list;
+ }
+}
+
+static void *
+really_install_handler (void *data)
+{
+ struct install_handler_data *args = data;
+ int signum = args->signum;
+ SCM thread = args->thread;
+ SCM handler = args->handler;
+ SCM cell;
+ SCM old_thread;
+
+ /* The following modifications are done while signals can be
+ delivered. That is not a real problem since the signal handler
+ will only touch the car of the handler cell and set the
+ pending_asyncs trigger of a thread. While the data structures
+ are in flux, the signal handler might store the wrong handler in
+ the cell, or set pending_asyncs of the wrong thread. We fix this
+ at the end by making sure that the cell has the right handler in
+ it, if any, and that pending_asyncs is set for the new thread.
+ */
+
+ /* Make sure we have a cell. */
+ cell = SCM_VECTOR_REF (signal_handler_cells, signum);
+ if (SCM_FALSEP (cell))
+ {
+ cell = scm_cons (SCM_BOOL_F, SCM_EOL);
+ SCM_VECTOR_SET (signal_handler_cells, signum, cell);
+ }
+
+ /* Make sure it is queued for the right thread. */
+ old_thread = SCM_VECTOR_REF (signal_handler_threads, signum);
+ if (!SCM_EQ_P (thread, old_thread))
+ {
+ scm_root_state *r;
+ if (!SCM_FALSEP (old_thread))
+ {
+ r = scm_i_thread_root (old_thread);
+ r->signal_asyncs = scm_delq_spine_x (cell, r->signal_asyncs);
+ }
+ if (!SCM_FALSEP (thread))
+ {
+ r = scm_i_thread_root (thread);
+ SCM_SETCDR (cell, r->signal_asyncs);
+ r->signal_asyncs = cell;
+ /* Set pending_asyncs just in case. A signal that is
+ delivered while we modify the data structures here might set
+ pending_asyncs of old_thread. */
+ r->pending_asyncs = 1;
+ }
+ SCM_VECTOR_SET (signal_handler_threads, signum, thread);
+ }
+
+ /* Set the new handler. */
+ if (SCM_FALSEP (handler))
+ {
+ SCM_VECTOR_SET (*signal_handlers, signum, SCM_BOOL_F);
+ SCM_VECTOR_SET (signal_cell_handlers, signum, SCM_BOOL_F);
+ }
+ else
+ {
+ SCM_VECTOR_SET (*signal_handlers, signum, handler);
+ SCM_VECTOR_SET (signal_cell_handlers, signum,
+ close_1 (handler, scm_int2num (signum)));
+ }
+
+ /* Now fix up the cell. It might contain the old handler but since
+ it is now queued for the new thread, we must make sure that the
+ new handler is run. Any signal that is delivered during the
+ following code will install the new handler, so we have no
+ problem.
+ */
+ if (!SCM_FALSEP (SCM_CAR (cell)))
+ SCM_SETCAR (cell, SCM_VECTOR_REF (signal_cell_handlers, signum));
+
+ /* Phfew. That should be it. */
+ return NULL;
+}
+
+static void
+install_handler (int signum, SCM thread, SCM handler)
+{
+ /* We block asyncs while installing the handler. It would be safe
+ to leave them on, but we might run the wrong handler should a
+ signal be delivered.
+ */
+
+ struct install_handler_data args;
+ args.signum = signum;
+ args.thread = thread;
+ args.handler = handler;
+ scm_c_call_with_blocked_asyncs (really_install_handler, &args);
+}
+
/* user interface for installation of signal handlers. */
SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
(SCM signum, SCM handler, SCM flags, SCM thread),
@@ -224,7 +355,11 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
if (SCM_UNBNDP (thread))
thread = scm_current_thread ();
else
- SCM_VALIDATE_THREAD (4, thread);
+ {
+ SCM_VALIDATE_THREAD (4, thread);
+ if (scm_c_thread_exited_p (thread))
+ SCM_MISC_ERROR ("thread has already exited", SCM_EOL);
+ }
#else
thread = SCM_BOOL_F;
#endif
@@ -243,7 +378,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
#else
chandler = (SIGRETTYPE (*) (int)) SCM_INUM (handler);
#endif
- SCM_VECTOR_SET (*signal_handlers, csig, SCM_BOOL_F);
+ install_handler (csig, SCM_BOOL_F, SCM_BOOL_F);
}
else
SCM_OUT_OF_RANGE (2, handler);
@@ -258,8 +393,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
{
action = orig_handlers[csig];
orig_handlers[csig].sa_handler = SIG_ERR;
- SCM_VECTOR_SET (*signal_handlers, csig, SCM_BOOL_F);
-
+ install_handler (csig, SCM_BOOL_F, SCM_BOOL_F);
}
#else
if (orig_handlers[csig] == SIG_ERR)
@@ -268,7 +402,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
{
chandler = orig_handlers[csig];
orig_handlers[csig] = SIG_ERR;
- SCM_VECTOR_SET (*signal_handlers, csig, SCM_BOOL_F);
+ install_handler (csig, SCM_BOOL_F, SCM_BOOL_F);
}
#endif
}
@@ -284,10 +418,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0,
if (orig_handlers[csig] == SIG_ERR)
save_handler = 1;
#endif
- SCM_VECTOR_SET (*signal_handlers, csig, handler);
- SCM_VECTOR_SET (signal_handler_cells, csig,
- scm_cons (close_1 (handler, signum), SCM_BOOL_F));
- SCM_VECTOR_SET (signal_handler_threads, csig, thread);
+ install_handler (csig, thread, handler);
}
/* XXX - Silently ignore setting handlers for `program error signals'
@@ -582,6 +713,8 @@ scm_init_scmsigs ()
scm_c_make_vector (NSIG, SCM_BOOL_F)));
signal_handler_cells =
scm_permanent_object (scm_c_make_vector (NSIG, SCM_BOOL_F));
+ signal_cell_handlers =
+ scm_permanent_object (scm_c_make_vector (NSIG, SCM_BOOL_F));
signal_handler_threads =
scm_permanent_object (scm_c_make_vector (NSIG, SCM_BOOL_F));