1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-11 14:21:10 +02:00

(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.
This commit is contained in:
Marius Vollmer 2002-10-27 20:25:17 +00:00
parent 402858a4d3
commit dbbaa07cab

View file

@ -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));