Why is a signal watcher active after default loop destruction and re-creation?

Jan-Philip Gehrcke jgehrcke at googlemail.com
Fri Nov 22 17:24:24 CET 2013


I am working on gevent/gipc and need to figure out why a libev signal 
watcher is still 'active' after destroying its originally assigned event 
loop. In the example code at the bottom of this mail, I

- create a signal watcher for SIGTERM
- assign it to the default event loop
- fork
- destroy the default event loop in the child
- create and run a new default event loop in the child
- send SIGTERM to the child

The output is:

> Msg from parent with PID 9934
> Msg from child with PID 9935
> Waiting for child.
> SIGTERM handler called in process 9935
> Child finished, exit parent.

I infer from this that

- the signal handler is still installed in the child (not surprising, 
since it is inherited)
- in the child, the signal watcher object is magically connected to the 
new loop

The latter surprises me. I went through the libev docs again and could 
not really find an explanation. Especially, I found this part:

"Destroying the default loop will "orphan" (not stop) all registered 
watchers, so you have to be careful not to execute code that modifies 
those watchers. Note also that in that case, you have to re-register any 
signal watchers."

What I indeed want is to "orphan" everything in the child related to the 
default loop created in the parent. However, it looks like signal 
watchers don't need to be re-registered, at least not for my test 
system. For some this might be a feature, but in my application case, 
this is a problem. I want to understand: why is that signal watcher 
still active?

And yes, an obvious solution would be `signal(SIGTERM, SIG_DFL);` in the 
child -- but what would the general approach be to make sure that 
previously installed watchers are not active anymore when their 
corresponding loop has been destroyed?

Thanks a lot for any insight,


The test program:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <ev.h>

void sigtermhandler(struct ev_loop *l, ev_signal *w, int revents) {
     printf("SIGTERM handler called in process %d\n", getpid());
     ev_break(l, EVBREAK_ALL);

void child(struct ev_loop *loop) {
     printf("Msg from child with PID %d\n", getpid());

     // Reset signal handler:
     //signal(SIGTERM, SIG_DFL);

     struct ev_loop *newloop = ev_default_loop(0);
     ev_run(newloop, 0);

int main (void) {
     printf("Msg from parent with PID %d\n", getpid());
     struct ev_loop *loop = EV_DEFAULT;

     // Install signal watcher in parent, handling SIGTERM.
     ev_signal signal_watcher;
     ev_signal_init(&signal_watcher, sigtermhandler, SIGTERM);
     ev_signal_start(loop, &signal_watcher);

     pid_t  pid;
     pid = fork();
     if (pid == -1) {
         fprintf(stderr, "Can't fork, error %d\n", errno);
     else if (pid == 0) {
     // Dirtywait until child is upnrunning, then send SIGTER.
     kill(pid, SIGTERM);
     //ev_run (loop, 0);
     printf("Waiting for child.\n");
     printf("Child finished, exit parent.\n");

More information about the libev mailing list