diff --git a/src/CHANGELOG b/src/CHANGELOG index 7d5abf99c..cfe3716b8 100755 --- a/src/CHANGELOG +++ b/src/CHANGELOG @@ -206,6 +206,9 @@ ECL 11.1.2 - ECL ships libffi together with its source tree, much like GMP and GC. + - On POSIX platforms ECL traps SIGCHLD and uses it to update the status of + external processes. + ;;; Local Variables: *** ;;; mode:text *** ;;; fill-column:79 *** diff --git a/src/c/main.d b/src/c/main.d index b92b0b936..6d0df2513 100644 --- a/src/c/main.d +++ b/src/c/main.d @@ -72,6 +72,7 @@ static cl_fixnum option_values[ECL_OPT_LIMIT+1] = { 1, /* ECL_OPT_TRAP_SIGILL */ 1, /* ECL_OPT_TRAP_SIGBUS */ 1, /* ECL_OPT_TRAP_SIGPIPE */ + 0, /* ECL_OPT_TRAP_SIGCHLD */ 1, /* ECL_OPT_TRAP_INTERRUPT_SIGNAL */ 1, /* ECL_OPT_SIGNAL_HANDLING_THREAD */ 128, /* ECL_OPT_SIGNAL_QUEUE_SIZE */ @@ -412,7 +413,9 @@ struct cl_core_struct cl_core = { Cnil, /* compiler_dispatch */ (cl_object)&default_rehash_size_data, /* rehash_size */ - (cl_object)&default_rehash_threshold_data /* rehash_threshold */ + (cl_object)&default_rehash_threshold_data, /* rehash_threshold */ + + Cnil /* external_processes */ }; int diff --git a/src/c/unixint.d b/src/c/unixint.d index 259e4ce1b..35c015373 100644 --- a/src/c/unixint.d +++ b/src/c/unixint.d @@ -345,6 +345,14 @@ handler_fn_protype(lisp_signal_handler, int sig, siginfo_t *info, void *aux) #ifdef SIGBUS case SIGBUS: return @'ext::segmentation-violation'; +#endif +#ifdef SIGCHLD + case SIGCHLD: { + cl_object status; + do { + status = si_external_process_wait(1, MAKE_FIXNUM(-1)); + } while (!Null(status) && status != @':error'); + } #endif default: return MAKE_FIXNUM(sig); @@ -1118,6 +1126,11 @@ install_synchronous_signal_handlers() mysignal(SIGPIPE, non_evil_signal_handler); } #endif +#ifdef SIGCHLD + if (ecl_get_option(ECL_OPT_TRAP_SIGCHLD)) { + mysignal(SIGCHLD, non_evil_signal_handler); + } +#endif } /* diff --git a/src/c/unixsys.d b/src/c/unixsys.d index af095770a..8e3ca5afa 100755 --- a/src/c/unixsys.d +++ b/src/c/unixsys.d @@ -19,6 +19,7 @@ #include #include #include +#include /* to see whether we have SIGCHLD */ #if !defined(_MSC_VER) # include #endif @@ -143,6 +144,48 @@ make_external_process(cl_object pid, cl_object input, cl_object output) return cl_funcall(4, @'ext::make-external-process', pid, input, output); } +#if defined(SIGCHLD) && !defined(ECL_MS_WINDOWS_HOST) +static void +add_external_process(cl_env_ptr env, cl_object process) +{ + ECL_WITH_GLOBAL_LOCK_BEGIN(env) { + cl_core.external_processes = ecl_cons(process, + cl_core.external_processes); + } ECL_WITH_GLOBAL_LOCK_END; +} + +static cl_object +find_external_process(cl_env_ptr env, cl_object pid) +{ + cl_object output = Cnil; + ECL_WITH_GLOBAL_LOCK_BEGIN(env) { + cl_object p; + for (p = cl_core.external_processes; p != Cnil; p = ECL_CONS_CDR(p)) { + cl_object process = ECL_CONS_CAR(p); + if (cl_funcall(2, @'ext::external-process-pid', + process) == pid) { + output = process; + break; + } + } + } ECL_WITH_GLOBAL_LOCK_END; + return output; +} + +static void +remove_external_process(cl_env_ptr env, cl_object process) +{ + ECL_WITH_GLOBAL_LOCK_BEGIN(env) { + cl_core.external_processes = + ecl_remove_eq(process, cl_core.external_processes); + } ECL_WITH_GLOBAL_LOCK_END; +} +#else +#define add_external_process(env,p) +#define remove_external_process(env,p) +#define find_external_process(env,p) Cnil +#endif + #if defined(ECL_MS_WINDOWS_HOST) cl_object si_close_windows_handle(cl_object h) @@ -165,6 +208,18 @@ make_windows_handle(HANDLE h) } #endif +static void +update_process_status(cl_env_ptr env, cl_object process, cl_object status, cl_object code) +{ + ecl_structure_set(process, @'ext::external-process', + 0, Cnil); + ecl_structure_set(process, @'ext::external-process', + 3, status); + ecl_structure_set(process, @'ext::external-process', + 4, code); + remove_external_process(env, process); +} + @(defun ext::external-process-wait (process_or_pid &optional (wait Cnil)) cl_object status, code; @ @@ -179,12 +234,7 @@ make_windows_handle(HANDLE h) } status = si_external_process_wait(2, pid, wait); code = VALUES(1); - ecl_structure_set(process_or_pid, @'ext::external-process', - 0, Cnil); - ecl_structure_set(process_or_pid, @'ext::external-process', - 3, status); - ecl_structure_set(process_or_pid, @'ext::external-process', - 4, code); + update_process_status(the_env, process_or_pid, status, code); } else { cl_object exit_status = Cnil; #if defined(ECL_MS_WINDOWS_HOST) @@ -208,7 +258,7 @@ make_windows_handle(HANDLE h) } ecl_enable_interrupts_env(the_env); #else - cl_index pid = fixint(process_or_pid); + cl_fixnum pid = fixint(process_or_pid); int code_int; int error = waitpid(pid, &code_int, Null(wait)? WNOHANG : 0); if (error < 0) { @@ -223,9 +273,19 @@ make_windows_handle(HANDLE h) } else if (WIFSTOPPED(code_int)) { status = @':stopped'; code = MAKE_FIXNUM(WSTOPSIG(code_int)); - } else { + } else if (error > 0) { status = @':running'; code = Cnil; + } else { + status = Cnil; + code = Cnil; + } + if (error > 0) { + cl_object process = + find_external_process(the_env, process_or_pid); + if (!Null(process)) { + update_process_status(the_env, process, status, code); + } } #endif } @@ -608,6 +668,7 @@ make_windows_handle(HANDLE h) stream_read = cl_core.null_stream; } process = make_external_process(pid, stream_write, stream_read); + add_external_process(the_env, process); if (!Null(wait)) { exit_status = si_external_process_wait(2, process, Ct); exit_status = VALUES(1); diff --git a/src/h/external.h b/src/h/external.h index 429c82ebd..83de2db1b 100755 --- a/src/h/external.h +++ b/src/h/external.h @@ -243,6 +243,8 @@ struct cl_core_struct { cl_object rehash_size; cl_object rehash_threshold; + + cl_object external_processes; }; extern ECL_API struct cl_core_struct cl_core; @@ -951,6 +953,7 @@ typedef enum { ECL_OPT_TRAP_SIGILL, ECL_OPT_TRAP_SIGBUS, ECL_OPT_TRAP_SIGPIPE, + ECL_OPT_TRAP_SIGCHLD, ECL_OPT_TRAP_INTERRUPT_SIGNAL, ECL_OPT_SIGNAL_HANDLING_THREAD, ECL_OPT_SIGNAL_QUEUE_SIZE,