This test program should print and endless row of "/" and "" characters, but instead it often crashes like this:
//////////////////\/////\///\////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\/Parent got SIGQUIT! /home/cederp/pike-fun/forked-signals.pike:10: /main()->handler_quit() /home/cederp/pike-fun/forked-signals.pike:33: /main()->arm_hup() -:1: Pike.Backend(0)->`()(3600.0) Child got SIGHUP! /home/cederp/pike-fun/forked-signals.pike:26: /main()->handler_hup() /home/cederp/pike-fun/forked-signals.pike:17: /main()->arm_quit() -:1: Pike.Backend(0)->`()(3600.0)
The cause: when Pike is compiled with NEED_SIGNAL_SAFE_FIFO the signal handler writes the signal number as a single byte to the write end of a pipe(2) set up by INIT_FIFO (in src/signal_handler.c). This pipe is set to close on exec -- but it doesn't close when the process forks.
So, if a Pike program uses fork, and one of the processes receives a signal, it is undefined if it is handled by the parent or the child process. Both processes will read from the same pipe.
I think this patch fixes the issue (comments welcome):
--- cut here --- Invoke signal handlers in the correct process after fork
When using fork() from Pike, the child needs to have its own pipe to send pending signals over. If it is shared with the parent, either the child or the parent may receive the signal.
Close the pipe and create a new one in the child after a fork. Do the same for the process wait info.
diff --git a/src/signal_handler.c b/src/signal_handler.c index dca9fe3..55b8a13 100644 --- a/src/signal_handler.c +++ b/src/signal_handler.c @@ -317,6 +317,7 @@ #define QUICK_CHECK_FIFO(pre,TYPE) ( PIKE_CONCAT(pre,_first) != PIKE_CONCAT(pre,_last) )
#define INIT_FIFO(pre,TYPE) +#define REINIT_FIFO(pre,TYPE)
#else /* NEED_SIGNAL_SAFE_FIFO */
@@ -361,6 +362,12 @@ set_close_on_exec(PIKE_CONCAT(pre,_fd)[1], 1); \ }while(0)
+#define REINIT_FIFO(pre,TYPE) do { \ + close(PIKE_CONCAT(pre,_fd)[0]); \ + close(PIKE_CONCAT(pre,_fd)[1]); \ + INIT_FIFO(pre,TYPE); \ +} while(0) + #endif /* else NEED_SIGNAL_SAFE_FIFO */
#ifndef SAFE_FIFO_DEBUG_END @@ -4373,6 +4380,10 @@ void Pike_f_fork(INT32 args) /* forked copy. there is now only one thread running, this one. */ num_threads=1; #endif + + REINIT_FIFO(sig, unsigned char); + REINIT_FIFO(wait,wait_data); + /* FIXME: Ought to clear pid_mapping here. */ call_callback(&fork_child_callback, 0); push_int(0); --- cut here ---
Perhaps REINIT_FIFO should reset the counters when !NEED_SIGNAL_SAFE_FIFO?
The test program:
--- cut here --- #!/usr/bin/env pike
int is_child = 0;
// This code runs in the child and repeatedly sends SIGQUIT to itself.
void handler_quit() { if (!is_child) error("Parent got SIGQUIT!\n"); werror("\"); call_out(arm_quit, 0.1); }
void arm_quit() { if (!kill(getpid(), signum("SIGQUIT"))) error("Failed to send QUIT: %s\n", strerror(errno())); }
// This code runs in the parent and repeatedly sends SIGHUP to itself.
void handler_hup() { if (is_child) error("Child got SIGHUP!\n"); werror("/"); call_out(arm_hup, 0.1); }
void arm_hup() { if (!kill(getpid(), signum("SIGHUP"))) error("Failed to send HUP: %s\n", strerror(errno())); }
// Setup code.
int main() { // Set up the same signal handlers in both the parent and the child. signal(signum("SIGHUP"), handler_hup); signal(signum("SIGQUIT"), handler_quit);
if (fork()) { // Start the parent. is_child = 0; call_out(arm_hup, 0.1); } else { // Start the child. is_child = 1; call_out(arm_quit, 0.1); }
// Both the parent and the child runs in the pike backend. return -1; }
pike-devel@lists.lysator.liu.se