Improved EXT:CATCH-SIGNAL to handle not running processes, threads and better use of sigprocmask.

This commit is contained in:
Juan Jose Garcia Ripoll 2011-08-06 22:47:39 +02:00
parent ccfb2673f9
commit bc7be9bb98
7 changed files with 137 additions and 61 deletions

View file

@ -94,6 +94,17 @@ ECL 11.7.1:
- Initialization of random number generator is done using only 16 bytes from
/dev/urandom (Phillip Marek).
- Each thread keeps a copy of the process sigmask (POSIX) and it is inherited
by children thread. The sigmask can be manipulated by the function
EXT:CATCH-SIGNAL which has the signature
(ext:catch-signal signal-code action &key process)
The ACTION is one of :IGNORE, :DEFAULT, :CATCH, determining what ECL does
when it receives the signal, or it can be :MASK/:UNMASK to determine whether
the process is blocking the signal or not. The optional argument :PROCESS
only applies to :MASK/:UNMASK and it can be the current process, some
process that has not been activated or any other value (indicating that
the function has a global effect, as sigprocmask).
;;; Local Variables: ***
;;; mode:text ***
;;; fill-column:79 ***

View file

@ -233,6 +233,19 @@ _ecl_alloc_env()
}
# endif
#endif
{
size_t bytes = cl_core.default_sigmask_bytes;
if (bytes == 0) {
output->default_sigmask = 0;
} else if (ecl_get_option(ECL_OPT_BOOTED)) {
output->default_sigmask = ecl_alloc_atomic(bytes);
memcpy(output->default_sigmask,
ecl_process_env()->default_sigmask,
bytes);
} else {
output->default_sigmask = cl_core.default_sigmask;
}
}
/*
* An uninitialized environment _always_ disables interrupts. They
* are activated later on by the thread entry point or init_unixint().
@ -404,6 +417,7 @@ struct cl_core_struct cl_core = {
Cnil, /* signal_queue */
NULL, /* default_sigmask */
0, /* default_sigmask_bytes */
#ifdef ECL_THREADS
0, /* last_var_index */

View file

@ -1305,6 +1305,7 @@ cl_symbols[] = {
{KEY_ "BASE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "BLOCK", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "CAPITALIZE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "CATCH", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "CASE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "CIRCLE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "COMPILE-TOPLEVEL", KEYWORD, NULL, -1, OBJNULL},
@ -1347,6 +1348,7 @@ cl_symbols[] = {
{KEY_ "IF-DOES-NOT-EXIST", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "IF-EXISTS", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "IF-OUTPUT-EXISTS", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "IGNORE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "IMPORT-FROM", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "INCLUDE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "INHERITED", KEYWORD, NULL, -1, OBJNULL},
@ -1370,6 +1372,7 @@ cl_symbols[] = {
{KEY_ "LOCAL",KEYWORD,NULL,-1,OBJNULL},
{KEY_ "LOCKABLE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "LOAD-TOPLEVEL", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "MASK", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "MISER-WIDTH", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "NAME", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "NAMED", KEYWORD, NULL, -1, OBJNULL},
@ -1392,6 +1395,7 @@ cl_symbols[] = {
{KEY_ "PRINT-FUNCTION", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "PRINT-OBJECT", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "PROBE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "PROCESS", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "RADIX", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "READABLY", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "REHASH-SIZE", KEYWORD, NULL, -1, OBJNULL},
@ -1417,6 +1421,7 @@ cl_symbols[] = {
{KEY_ "TEST-NOT", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "TYPE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "UNSPECIFIC", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "UNMASK", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "UP", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "UPCASE", KEYWORD, NULL, -1, OBJNULL},
{KEY_ "USE", KEYWORD, NULL, -1, OBJNULL},

View file

@ -1305,6 +1305,7 @@ cl_symbols[] = {
{KEY_ "BASE",NULL},
{KEY_ "BLOCK",NULL},
{KEY_ "CAPITALIZE",NULL},
{KEY_ "CATCH",NULL},
{KEY_ "CASE",NULL},
{KEY_ "CIRCLE",NULL},
{KEY_ "COMPILE-TOPLEVEL",NULL},
@ -1347,6 +1348,7 @@ cl_symbols[] = {
{KEY_ "IF-DOES-NOT-EXIST",NULL},
{KEY_ "IF-EXISTS",NULL},
{KEY_ "IF-OUTPUT-EXISTS",NULL},
{KEY_ "IGNORE",NULL},
{KEY_ "IMPORT-FROM",NULL},
{KEY_ "INCLUDE",NULL},
{KEY_ "INHERITED",NULL},
@ -1370,6 +1372,7 @@ cl_symbols[] = {
{KEY_ "LOCAL",NULL},
{KEY_ "LOCKABLE",NULL},
{KEY_ "LOAD-TOPLEVEL",NULL},
{KEY_ "MASK",NULL},
{KEY_ "MISER-WIDTH",NULL},
{KEY_ "NAME",NULL},
{KEY_ "NAMED",NULL},
@ -1392,6 +1395,7 @@ cl_symbols[] = {
{KEY_ "PRINT-FUNCTION",NULL},
{KEY_ "PRINT-OBJECT",NULL},
{KEY_ "PROBE",NULL},
{KEY_ "PROCESS",NULL},
{KEY_ "RADIX",NULL},
{KEY_ "READABLY",NULL},
{KEY_ "REHASH-SIZE",NULL},
@ -1417,6 +1421,7 @@ cl_symbols[] = {
{KEY_ "TEST-NOT",NULL},
{KEY_ "TYPE",NULL},
{KEY_ "UNSPECIFIC",NULL},
{KEY_ "UNMASK",NULL},
{KEY_ "UP",NULL},
{KEY_ "UPCASE",NULL},
{KEY_ "USE",NULL},

View file

@ -427,7 +427,7 @@ mp_process_enable(cl_object process)
#ifdef HAVE_SIGPROCMASK
{
sigset_t previous;
pthread_sigmask(SIG_SETMASK, cl_core.default_sigmask, &previous);
pthread_sigmask(SIG_SETMASK, process_env->default_sigmask, &previous);
code = pthread_create(&process->process.thread, &pthreadattr,
thread_entry_point, process);
pthread_sigmask(SIG_SETMASK, &previous, NULL);

View file

@ -199,6 +199,7 @@ static struct {
};
#ifdef HAVE_SIGPROCMASK
static sigset_t main_thread_sigmask;
# define handler_fn_protype(name, sig, info, aux) name(sig, info, aux)
# define call_handler(name, sig, info, aux) name(sig, info, aux)
# define reinstall_signal(x,y) mysignal(x,y)
@ -206,21 +207,26 @@ static struct {
static void
mysignal(int code, void (*handler)(int, siginfo_t *, void*))
{
struct sigaction new_action, old_action;
struct sigaction action;
sigaction(code, NULL, &action);
if (handler == SIG_IGN || handler == SIG_DFL) {
action.sa_handler = handler;
} else {
#ifdef SA_SIGINFO
new_action.sa_sigaction = handler;
new_action.sa_flags = SA_SIGINFO;
action.sa_sigaction = handler;
action.sa_flags = SA_SIGINFO;
# if 0 && defined(SA_ONSTACK)
if (code == SIGSEGV) {
new_action.sa_flags |= SA_ONSTACK;
}
if (code == SIGSEGV) {
action.sa_flags |= SA_ONSTACK;
}
# endif
#else
new_action.sa_handler = handler;
new_action.sa_flags = 0;
action.sa_handler = handler;
action.sa_flags = 0;
#endif
sigfillset(&new_action.sa_mask);
sigaction(code, &new_action, &old_action);
sigfillset(&action.sa_mask);
}
sigaction(code, &action, NULL);
}
#else /* HAVE_SIGPROCMASK */
# define handler_fn_protype(name, sig, info, aux) name(sig)
@ -359,9 +365,9 @@ unblock_signal(int signal)
* ECL's default sigmask.
*/
# ifdef ECL_THREADS
pthread_sigmask(SIG_SETMASK, cl_core.default_sigmask, NULL);
pthread_sigmask(SIG_SETMASK, ecl_process_env()->default_sigmask, NULL);
# else
sigprocmask(SIG_SETMASK, cl_core.default_sigmask, NULL);
sigprocmask(SIG_SETMASK, ecl_process_env()->default_sigmask, NULL);
# endif
}
#endif
@ -661,7 +667,74 @@ ecl_check_pending_interrupts(void)
}
}
@(defun ext::catch-signal (code flag &key local)
static cl_object
do_catch_signal(int code, cl_object action, cl_object process)
{
if (action == Cnil || action == @':ignore') {
mysignal(code, SIG_IGN);
return Ct;
} else if (action == @':default') {
mysignal(code, SIG_DFL);
return Ct;
} else if (action == Ct || action == @':catch') {
if (code == SIGSEGV) {
mysignal(code, sigsegv_handler);
}
#ifdef SIGBUS
else if (code == SIGBUS) {
mysignal(code, sigbus_handler);
}
#endif
#ifdef SIGCHLD
else if (code == SIGCHLD) {
# ifndef ECL_THREADS
mysignal(SIGCHLD, non_evil_signal_handler);
# endif
}
#endif
else {
mysignal(code, non_evil_signal_handler);
}
}
#ifdef HAVE_SIGPROCMASK
# ifdef ECL_THREADS
if (type_of(process) == t_process) {
cl_env_ptr env = process->process.env;
sigset_t *handled_set = (sigset_t *)env->default_sigmask;
if (action == @':mask') {
sigaddset(handled_set, code);
} else if (action == @':unmask') {
sigdelset(handled_set, code);
} else {
return do_catch_signal(code, Ct, process);
}
if (env == ecl_process_env()) {
pthread_sigmask(SIG_SETMASK, handled_set, NULL);
}
return Ct;
}
# endif
{
sigset_t handled_set;
sigprocmask(SIG_SETMASK, NULL, &handled_set);
if (action == @':mask') {
sigaddset(&handled_set, code);
printf(";;; %d masked\n", code);
} else if (action == @':unmask') {
sigdelset(&handled_set, code);
printf(";;; %d unmasked\n", code);
} else {
return do_catch_signal(code, Ct, process);
}
sigprocmask(SIG_SETMASK, &handled_set, NULL);
return Ct;
}
#else
return Cnil;
#endif
}
@(defun ext::catch-signal (code flag &key process)
@
{
cl_object output = Cnil;
@ -687,40 +760,7 @@ ecl_check_pending_interrupts(void)
#endif
for (i = 0; known_signals[i].code >= 0; i++) {
if (known_signals[i].code == code_int) {
output = Ct;
#if defined(ECL_THREADS) && defined(HAVE_SIGPROCMASK)
if (!Null(local)) {
sigset_t handled_set;
pthread_sigmask(SIG_SETMASK, NULL, &handled_set);
if (Null(flag)) {
sigdelset(&handled_set, code_int);
} else {
sigaddset(&handled_set, code_int);
}
pthread_sigmask(SIG_SETMASK, &handled_set, NULL);
break;
}
#endif
if (Null(flag)) {
signal(code_int, SIG_DFL);
} else if (code_int == SIGSEGV) {
mysignal(code_int, sigsegv_handler);
}
#ifdef SIGBUS
else if (code_int == SIGBUS) {
mysignal(code_int, sigbus_handler);
}
#endif
#ifdef SIGCHLD
else if (code_int == SIGCHLD) {
#ifndef ECL_THREADS
mysignal(SIGCHLD, non_evil_signal_handler);
#endif
}
#endif
else {
mysignal(code_int, non_evil_signal_handler);
}
output = do_catch_signal(code_int, flag, process);
break;
}
}
@ -1039,17 +1079,17 @@ install_asynchronous_signal_handlers()
# endif
#endif
#ifdef HAVE_SIGPROCMASK
static sigset_t sigmask;
sigset_t *sigmask = cl_core.default_sigmask = &main_thread_sigmask;
cl_core.default_sigmask_bytes = sizeof(sigset_t);
# ifdef ECL_THREADS
pthread_sigmask(SIG_SETMASK, NULL, &sigmask);
pthread_sigmask(SIG_SETMASK, NULL, sigmask);
# else
sigprocmask(SIG_SETMASK, NULL, &sigmask);
sigprocmask(SIG_SETMASK, NULL, sigmask);
# endif
#endif
cl_core.default_sigmask = NULL;
#ifdef SIGINT
if (ecl_get_option(ECL_OPT_TRAP_SIGINT)) {
async_handler(SIGINT, non_evil_signal_handler, &sigmask);
async_handler(SIGINT, non_evil_signal_handler, sigmask);
}
#endif
#ifdef SIGCHLD
@ -1057,16 +1097,15 @@ install_asynchronous_signal_handlers()
/* We have to set the process signal handler explicitly,
* because on many platforms the default is SIG_IGN. */
mysignal(SIGCHLD, lisp_signal_handler);
async_handler(SIGCHLD, lisp_signal_handler, &sigmask);
async_handler(SIGCHLD, lisp_signal_handler, sigmask);
}
#endif
#ifdef HAVE_SIGPROCMASK
# if defined(ECL_THREADS)
pthread_sigmask(SIG_SETMASK, &sigmask, NULL);
pthread_sigmask(SIG_SETMASK, sigmask, NULL);
# else
sigprocmask(SIG_SETMASK, &sigmask, NULL);
sigprocmask(SIG_SETMASK, sigmask, NULL);
# endif
cl_core.default_sigmask = &sigmask;
#endif
#ifdef ECL_WINDOWS_THREADS
old_W32_exception_filter =
@ -1086,6 +1125,7 @@ static void
install_signal_handling_thread()
{
#if defined(ECL_THREADS) && defined(HAVE_SIGPROCMASK)
ecl_process_env()->default_sigmask = &main_thread_sigmask;
if (ecl_get_option(ECL_OPT_SIGNAL_HANDLING_THREAD)) {
cl_object fun =
ecl_make_cfun((cl_objectfn_fixed)
@ -1128,8 +1168,8 @@ install_process_interrupt_handler()
}
mysignal(signal, non_evil_signal_handler);
#ifdef HAVE_SIGROCMASK
sigdelset(cl_core.default_sigmask, signal);
pthread_sigmask(SIG_SETMASK, cl_core.default_sigmask, NULL);
sigdelset(ecl_process_env()->default_sigmask, signal);
pthread_sigmask(SIG_SETMASK, ecl_process_env()->default_sigmask, NULL);
#endif
}
#endif

View file

@ -95,6 +95,7 @@ struct cl_env_struct {
cl_object own_process;
#endif
cl_object pending_interrupt;
void *default_sigmask;
/* The following is a hash table for caching invocations of
generic functions. In a multithreaded environment we must
@ -231,8 +232,8 @@ struct cl_core_struct {
cl_object signal_queue_lock;
#endif
cl_object signal_queue;
void *default_sigmask;
void *default_sigmask;
cl_index default_sigmask_bytes;
#ifdef ECL_THREADS
cl_index last_var_index;