diff options
Diffstat (limited to 'libguile/scmsigs.c')
-rw-r--r-- | libguile/scmsigs.c | 251 |
1 files changed, 110 insertions, 141 deletions
diff --git a/libguile/scmsigs.c b/libguile/scmsigs.c index 618506ea7..5ed5091ac 100644 --- a/libguile/scmsigs.c +++ b/libguile/scmsigs.c @@ -23,6 +23,7 @@ #endif #include <signal.h> +#include <stdio.h> #include <errno.h> #include "libguile/_scm.h" @@ -69,21 +70,24 @@ /* take_signal is installed as the C signal handler whenever a Scheme - handler is set. when a signal arrives, take_signal will queue the - Scheme handler procedure for its thread. */ + handler is set. When a signal arrives, take_signal will write a + byte into the 'signal pipe'. The 'signal delivery thread' will + read this pipe and queue the appropriate asyncs. + + When Guile is built without threads, the signal handler will + install the async directly. +*/ /* Scheme vectors with information about a signal. signal_handlers - contains the handler procedure or #f, signal_handler_cells contains - 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 + contains the handler procedure or #f, signal_handler_asyncs + contains the thunk to be marked as an async when the signal arrives + (or the cell with the thunk in a singlethreaded Guile), 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_asyncs; static SCM signal_handler_threads; /* saves the original C handlers, when a new handler is installed. @@ -94,164 +98,128 @@ static struct sigaction orig_handlers[NSIG]; static SIGRETTYPE (*orig_handlers[NSIG])(int); #endif +static SCM +close_1 (SCM proc, SCM arg) +{ + return scm_primitive_eval_x (scm_list_3 (scm_sym_lambda, SCM_EOL, + scm_list_2 (proc, arg))); +} + +#if SCM_USE_PTHREAD_THREADS + +static int signal_pipe[2]; static SIGRETTYPE take_signal (int signum) { - if (signum >= 0 && signum < NSIG) - { - SCM cell = SCM_SIMPLE_VECTOR_REF (signal_handler_cells, signum); - SCM handler = SCM_SIMPLE_VECTOR_REF (signal_cell_handlers, signum); - SCM thread = SCM_SIMPLE_VECTOR_REF (signal_handler_threads, signum); - scm_root_state *root = scm_i_thread_root (thread); - if (scm_is_pair (cell)) - { - SCM_SETCAR (cell, handler); - root->pending_asyncs = 1; - } - } - + char sigbyte = signum; + write (signal_pipe[1], &sigbyte, 1); + #ifndef HAVE_SIGACTION signal (signum, take_signal); #endif } -SCM -scm_sigaction (SCM signum, SCM handler, SCM flags) +static SCM +signal_delivery_thread (void *data) { - return scm_sigaction_for_thread (signum, handler, flags, SCM_UNDEFINED); + sigset_t all_sigs; + scm_t_guile_ticket ticket; + int n, sig; + char sigbyte; + + sigfillset (&all_sigs); + scm_i_pthread_sigmask (SIG_SETMASK, &all_sigs, NULL); + + while (1) + { + ticket = scm_leave_guile (); + n = read (signal_pipe[0], &sigbyte, 1); + sig = sigbyte; + scm_enter_guile (ticket); + if (n == 1 && sig >= 0 && sig < NSIG) + { + SCM h, t; + + h = SCM_SIMPLE_VECTOR_REF (signal_handler_asyncs, sig); + t = SCM_SIMPLE_VECTOR_REF (signal_handler_threads, sig); + if (scm_is_true (h)) + scm_system_async_mark_for_thread (h, t); + } + else if (n < 0 && errno != EINTR) + perror ("error in signal delivery thread"); + } } -static SCM -close_1 (SCM proc, SCM arg) +static void +start_signal_delivery_thread (void) { - return scm_primitive_eval_x (scm_list_3 (scm_sym_lambda, SCM_EOL, - scm_list_2 (proc, arg))); + if (pipe (signal_pipe) != 0) + scm_syserror (NULL); + scm_spawn_thread (signal_delivery_thread, NULL, + scm_handle_by_message, "signal delivery thread"); } -/* 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). -*/ +static void +ensure_signal_delivery_thread () +{ + static scm_i_pthread_once_t once = SCM_I_PTHREAD_ONCE_INIT; + scm_i_pthread_once (&once, start_signal_delivery_thread); +} -struct install_handler_data { - int signum; - SCM thread; - SCM handler; -}; +#else /* !SCM_USE_PTHREAD_THREADS */ -static SCM -scm_delq_spine_x (SCM cell, SCM list) +static SIGRETTYPE +take_signal (int signum) { - SCM s = list, prev = SCM_BOOL_F; - - while (!scm_is_eq (cell, s)) - { - if (scm_is_null (s)) - return list; - prev = s; - s = SCM_CDR (s); - } - if (scm_is_false (prev)) - return SCM_CDR (cell); - else + SCM cell = SCM_SIMPLE_VECTOR_REF (signal_handler_asyncs, signum); + scm_i_thread *t = SCM_I_CURRENT_THREAD; + + if (scm_is_false (SCM_CDR (cell))) { - SCM_SETCDR (prev, SCM_CDR (cell)); - return list; + SCM_SETCDR (cell, t->active_asyncs); + t->active_asyncs = cell; + t->pending_asyncs = 1; } + +#ifndef HAVE_SIGACTION + signal (signum, take_signal); +#endif } -static void * -really_install_handler (void *data) +static void +ensure_signal_delivery_thread () { - 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_SIMPLE_VECTOR_REF (signal_handler_cells, signum); - if (scm_is_false (cell)) - { - cell = scm_cons (SCM_BOOL_F, SCM_EOL); - SCM_SIMPLE_VECTOR_SET (signal_handler_cells, signum, cell); - } + return; +} - /* Make sure it is queued for the right thread. */ - old_thread = SCM_SIMPLE_VECTOR_REF (signal_handler_threads, signum); - if (!scm_is_eq (thread, old_thread)) - { - scm_root_state *r; - if (scm_is_true (old_thread)) - { - r = scm_i_thread_root (old_thread); - r->signal_asyncs = scm_delq_spine_x (cell, r->signal_asyncs); - } - if (scm_is_true (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_SIMPLE_VECTOR_SET (signal_handler_threads, signum, thread); - } +#endif /* !SCM_USE_PTHREAD_THREADS */ - /* Set the new handler. */ +static void +install_handler (int signum, SCM thread, SCM handler) +{ if (scm_is_false (handler)) { SCM_SIMPLE_VECTOR_SET (*signal_handlers, signum, SCM_BOOL_F); - SCM_SIMPLE_VECTOR_SET (signal_cell_handlers, signum, SCM_BOOL_F); + SCM_SIMPLE_VECTOR_SET (signal_handler_asyncs, signum, SCM_BOOL_F); } else { + SCM async = close_1 (handler, scm_from_int (signum)); +#if !SCM_USE_PTHREAD_THREADS + async = scm_cons (async, SCM_BOOL_F); +#endif SCM_SIMPLE_VECTOR_SET (*signal_handlers, signum, handler); - SCM_SIMPLE_VECTOR_SET (signal_cell_handlers, signum, - close_1 (handler, scm_from_int (signum))); + SCM_SIMPLE_VECTOR_SET (signal_handler_asyncs, signum, async); } - /* 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_is_true (SCM_CAR (cell))) - SCM_SETCAR (cell, SCM_SIMPLE_VECTOR_REF (signal_cell_handlers, signum)); - - /* Phfew. That should be it. */ - return NULL; + SCM_SIMPLE_VECTOR_SET (signal_handler_threads, signum, thread); } -static void -install_handler (int signum, SCM thread, SCM handler) +SCM +scm_sigaction (SCM signum, SCM handler, SCM flags) { - /* 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); + return scm_sigaction_for_thread (signum, handler, flags, SCM_UNDEFINED); } /* user interface for installation of signal handlers. */ @@ -323,19 +291,22 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0, SCM_MISC_ERROR ("thread has already exited", SCM_EOL); } - SCM_DEFER_INTS; + ensure_signal_delivery_thread (); + + SCM_CRITICAL_SECTION_START; old_handler = SCM_SIMPLE_VECTOR_REF (*signal_handlers, csig); if (SCM_UNBNDP (handler)) query_only = 1; else if (scm_is_integer (handler)) { - if (SCM_NUM2LONG (2, handler) == (long) SIG_DFL - || SCM_NUM2LONG (2, handler) == (long) SIG_IGN) + long handler_int = scm_to_long (handler); + + if (handler_int == (long) SIG_DFL || handler_int == (long) SIG_IGN) { #ifdef HAVE_SIGACTION - action.sa_handler = (SIGRETTYPE (*) (int)) scm_to_long (handler); + action.sa_handler = (SIGRETTYPE (*) (int)) handler_int; #else - chandler = (SIGRETTYPE (*) (int)) scm_to_int (handler); + chandler = (SIGRETTYPE (*) (int)) handler_int; #endif install_handler (csig, SCM_BOOL_F, SCM_BOOL_F); } @@ -425,7 +396,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0, } if (old_action.sa_handler == SIG_DFL || old_action.sa_handler == SIG_IGN) old_handler = scm_from_long ((long) old_action.sa_handler); - SCM_ALLOW_INTS; + SCM_CRITICAL_SECTION_END; return scm_cons (old_handler, scm_from_int (old_action.sa_flags)); #else if (query_only) @@ -444,7 +415,7 @@ SCM_DEFINE (scm_sigaction_for_thread, "sigaction", 1, 3, 0, } if (old_chandler == SIG_DFL || old_chandler == SIG_IGN) old_handler = scm_from_long ((long) old_chandler); - SCM_ALLOW_INTS; + SCM_CRITICAL_SECTION_END; return scm_cons (old_handler, scm_from_int (0)); #endif } @@ -601,7 +572,7 @@ SCM_DEFINE (scm_sleep, "sleep", 1, 0, 0, "of seconds remaining otherwise.") #define FUNC_NAME s_scm_sleep { - return scm_from_ulong (scm_thread_sleep (scm_to_int (i))); + return scm_from_uint (scm_std_sleep (scm_to_uint (i))); } #undef FUNC_NAME @@ -610,7 +581,7 @@ SCM_DEFINE (scm_usleep, "usleep", 1, 0, 0, "Sleep for @var{i} microseconds.") #define FUNC_NAME s_scm_usleep { - return scm_from_ulong (scm_thread_usleep (scm_to_ulong (i))); + return scm_from_ulong (scm_std_usleep (scm_to_ulong (i))); } #undef FUNC_NAME @@ -636,9 +607,7 @@ scm_init_scmsigs () signal_handlers = SCM_VARIABLE_LOC (scm_c_define ("signal-handlers", 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 = + signal_handler_asyncs = 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)); |