Domanda

I am trying to implement some code using AnyEvent and EV. I would like to install a child watcher ( Mockup of what I want done below)

use Modern::Perl;
use AnyEvent;
use EV;

my $SIG{QUIT} = \&kill_child;
my $exit_status = undef;
my $pid = fork or exec 'child.pl';
my $w = AnyEvent->child(pid => $pid, cb => sub { $exit_status = $_[2]; cleanup_after_child(); });
my $t = AE::timer 0, 5, sub { say $exit_status; fork_another_process(); };

AnyEvent->condvar->recv;

sub kill_child{
    foreach my $signal (qw(HUP QUIT INT KILL)){
        if(child_alive()){
            kill($signal,$pid);
            sleep 1;
        }
    }
    exit; #Probably wrong
}

I would like some help writing the child_alive() as well as figuring out when various callback as well as placing an exit in kill_child(). I need to make sure that cleanup_after_child() is called before the process exits.

Would using an AnyEvent Signal watcher help? My goal is to be able to safely shut down a daemon process, by first killing the processes children and cleaning up after them. I have asked a related question earliyer (Waiting on a child process in perl) and would like to prevent that bug from occuring during the exit handling.

Thanks

EDIT: Would the following work for a child_alive() function?

sub child_alive{
    return defined($exit_status)?1:0;
}

In other words, would the callback have already been called once the child exited, or would the callback only be executed on the next iteration of the EventLoop? if so, How can I exit only after all the signals have been handled? do I just delete all the events like:

$w = undef;
$t = undef;

sorry, I am more of trying to figure out how AnyEvent will handle these signals rather than how signals work in general.

È stato utile?

Soluzione

You can use a hash to keep track of the children you have.

$children{$pid} = 1;       # On creation.

delete($children{$pid});   # On reaping.

keys(%children)            # Lists existing children.

So all that's left is waiting for all the children to complete, and there's already an example of that in AnyEvent->child's documentation.

All together, we get

my $done = AnyEvent->condvar;
my %children;
my $spawner;

sub ae_sleep {
   my ($dur) = @_;
   my $done = AnyEvent->condvar;
   my $t = AnyEvent->timer(after => $dur, cb => sub { $done->send });
   $done->recv;
}

sub kill_children {
   for my $sig (qw( HUP QUIT INT KILL )) {
      last if !%children;
      kill($sig => $_) for keys(%children);
      ae_sleep(1);
   }
}

$SIG{QUIT} = sub {
   $spawner = undef;   # Stop creating new children.
   kill_children();    # Kill existing children.
   $done->recv;        # Wait for children to be reaped.
   exit(0);
}

sub create_child {
   my $pid = ...;
   $done->begin;
   $children{$pid} = AnyEvent->child(
      pid => $pid,
      cb  => sub {
         my ($pid, $status) = @_;
         delete($children{$pid});
         warn "pid $pid exited with status $status";
         $done->end;
      },
   );
}

$spawner = AnyEvent->timer(
   after    => 0,
   interval => 5,
   cb       => \&create_child.
);

AnyEvent->condvar->recv; # Wait "for ever".

Altri suggerimenti

From perldoc -f kill:

If SIGNAL is either the number 0 or the string "ZERO" (or "SIGZZERO"), no signal is sent to the process, but "kill" checks whether it's possible to send a signal to it (that means, to be brief, that the process is owned by the same user, or we are the super-user). This is useful to check that a child process is still alive (even if only as a zombie) and hasn't changed its UID. See perlport for notes on the portability of this construct.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top