Greg Nisbet
Greg Nisbet

Reputation: 6994

Perl forward SIGINT to forked child process

So I am trying to create a Perl program that forks a worker and waits for it to finish. In my real use case I need to fork many workers and wait for them all, so I thought I would try a test case with a single worker. My concern here is that when I run this program in the terminal, sending a ^C doesn't kill the parent, even though the signal handler seems like it should reap the child process and cause the parent to exit normally. I am trying to use waitpid to keep the parent alive so that it can receive signals and pass them along to the child, but the parent process seems to ignore ^C entirely.

use strict;
use warnings FATAL => 'all';
use POSIX ":sys_wait_h";

my $cpid;

sub reap_children_and_exit {
    defined $cpid and kill 'HUP', $cpid;
    exit 0;
}

$SIG{INT} = \&reap_children_and_exit;

my $child_text = <<'EOF';
perl -e 'while (1) { printf "%s\n", rand() }'
EOF

$cpid = fork;
if ($cpid == 0) {
    exec($child_text)
}

waitpid($cpid, WNOHANG);

Upvotes: 1

Views: 851

Answers (2)

eddy85br
eddy85br

Reputation: 294

If you don't need to implement all by yourself and can use a module, I would recommend: https://metacpan.org/pod/Parallel::Prefork This module wraps all the worker/children creation and management for you easily, besides, it saves memory usage.

If you plan to create a daemon, there is another one, that can manage forks: https://metacpan.org/pod/Daemon::Control

Or try this solution:

use strict;
use warnings FATAL => 'all';
use feature 'say';
use POSIX ":sys_wait_h";

BEGIN {
    sub reap_children_and_exit {
        my $signame = shift;
        my $pid = shift;
        defined $pid and kill $signame, $pid;
        say "$pid => Received '$signame' !";
        exit 0;
    }
    $SIG{INT} = \&reap_children_and_exit;
}

my %children;

$SIG{CHLD} = sub {
    # don't change $! and $? outside handler
    local ($!, $?);
    while ( (my $pid = waitpid(-1, WNOHANG)) > 0 ) {
        delete $children{$pid};
        reap_children_and_exit('HUP', $pid);
    }
};

my $child_text = <<'EOF';
perl -e 'while (1) { printf "%s\n", rand(); sleep 1; }'
EOF

while (1) {
    my $pid = fork();
    die "Cannot fork" unless defined $pid;
    say "I'm the PID $pid";
    if ($pid == 0) {
        say q{I'm the parent};
        exit 0;
    } else {
        $children{$pid} = 1;
        system($child_text);
    }
}

I wish this helps you. Best regards!

Upvotes: 1

ikegami
ikegami

Reputation: 386501

I am trying to use waitpid to keep the parent alive

You told waitpid to return immediately. Change WNOHANG to 0.

Upvotes: 3

Related Questions