The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use POE;
use POE::Session;
use POE::Kernel;
use POE::Component::ProcTerminator;
use POE::Sugar::Attributes;
use POSIX qw(:sys_wait_h :errno_h :signal_h);
use Time::HiRes qw(sleep time);

use base qw(POE::Sugar::Attributes);
my $poe_kernel = "POE::Kernel";

my $CALL_NEXT;
my $BEGIN_TIME;
my $LAST_EXIT;

sub child_dead :Reaper {
    my ($pid,$status) = @_[ARG1,ARG2];
    diag "Child $pid reaped. Signal: ". WTERMSIG($status);
    $LAST_EXIT = WTERMSIG($status);
    if ($CALL_NEXT) {
        $CALL_NEXT->();
    } else {
        die("Couldn't find 'next' function!");
    }
}


sub got_signit :SigHandler(INT) {
    warn("Stopping...");
    $_[KERNEL]->signal($_[KERNEL], 'UIDESTROY');
}

sub fork_and_schedule {
    my ($sigs_to_send, $sigs_to_ignore,$grace) = @_;
    $grace ||= 0.5;
    local %SIG = %SIG;
    foreach my $sig (@$sigs_to_ignore) {
        $SIG{$sig} = 'IGNORE';
    }
    
    my $pid = fork();
    
    alarm(10);
    if ($pid == 0){
        POSIX::pause() while 1;
    } else {
        alarm(0);
        diag "Forked $pid";
        $poe_kernel->call(proc_terminator =>
                         terminate => $pid,
                         {
                            siglist => $sigs_to_send,
                            grace_period => $grace,
                         });
    }
}

sub poe_start :Start {
    POE::Component::ProcTerminator->spawn(Alias => "proc_terminator");
    fork_and_schedule([SIGINT, SIGUSR1, SIGKILL], [qw(INT USR1)], 0.5);
    $BEGIN_TIME = time();
    $CALL_NEXT = sub {
        my $duration = time - $BEGIN_TIME;
        ok($duration < 2, "Slept a bit");
        is($LAST_EXIT, SIGKILL, "Child died with SIGKILL");
        fork_many();
    }
}

sub fork_many {
    $BEGIN_TIME = time();
    foreach (1..10) {
        fork_and_schedule([SIGQUIT], [], 0.1);
    }
    my $count_total = 0;
    $CALL_NEXT = sub {
        $count_total++;
        is($LAST_EXIT, SIGQUIT, "Exited with SIGQUIT");
        if ($count_total == 10) {
            ok(time - $BEGIN_TIME < 1, "Took less than a second!");
        }
    }
}

POE::Sugar::Attributes->wire_new_session("main_session");
POE::Kernel->run();

done_testing();