Question

Updated:

I have a unittest (consider it X). There is a subprocess (Y). Y does not explicitly fork. X forks so it can exec Y. Here is X:

warn("Starting Y...");
my $pid;

die "failed to fork" unless($pid = fork);
unless($pid) {
    { exec "exec /usr/bin/Y"; };
    warn "failed: $!";
    _exit(0);
}

# Do something to test Y.

warn("Stopping Y...");
my $st;
do {
    kill(15, $pid);
    $st = waitpid($pid, WNOHANG);
} while ($st > 0);

warn("Y has stopped");

The output I get from X is:

Starting Y... at ...
Some stuff.
Stopping Y.... at ...
Y has stopped at ...

This would suggest Y has received the signal and stopped. But Y doesn't stop, it goes into a defunct state (what I previously referred to as "zombie like"). As far as I can tell, it is a finished process, not available to kill(0, $pid) but visible through ps and /proc.

In the actual process Y, it is a different story:

sub goodbye {
    warn("Received signal");
    exit(0);
}

$SIG{TERM} = \&goodbye;
$SIG{INT} = \&goodbye;

warn("Starting server...");
my $d = HTTP::Daemon->new(
    LocalAddr => "127.0.0.1",
    LocalPort => 81,
    Reuse => 1
);

while (my $c = $d->accept) {
    # Do some stuff

    $c->close;
}

warn("Exiting...");

I never see the Exiting... nor the Received signal in the output for Y, only the Starting server message. Y is functioning and accepts all connections from X, thus passes the tests; it just fails to stop.

This is the output from some debug before and after X signals Y.

DEBUG before kill:
root      2843  2795  0 11:23 pts/4    00:00:00 /usr/bin/perl /bin/Y
root      2844  2843  4 11:23 pts/4    00:00:00 /usr/bin/perl /bin/Y

DEBUG after kill:
root      2843  2795  0 11:23 pts/4    00:00:00 [Y] <defunct>
root      2844     1  4 11:23 pts/4    00:00:00 /usr/bin/perl /bin/Y

Note the defunct state of Y and notice that there are two processes. I didn't start two and it doesn't fork, so I am assuming HTTP::Daemon forked. I explicitly modified X to send a different signal. This time I sent SIGINT, like when I hit Ctrl-C and it actually stops the hung X and both Ys. I get the Signal received message from Y this time, but it stills goes into a defunct state and there are still two processes.

My question is targeted at HTTP::Daemon as opposed to Perl. What on earth is HTTP::Daemon (derived from IO::Socket::INET) actually doing to cause this chaos and why? Secondly, how do I adapt Y to cope with what HTTP::Daemon is doing?

Was it helpful?

Solution

Okay, I may not have asked this question in the best possible way the first time round. But that doesn't mean it wasn't a valid one for which I knew that someone out there may hold the answer. But yet again, here I am, fulfilling the answer to my own question. Rather than regurgitate the answer I found, here it is in all its glory:

Strange blocking issue with HTTP::Daemon

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top