1 package MooseX::Runnable::Invocation::Plugin::Restart::Base;
3 use MooseX::Types::Moose qw(Int);
4 use namespace::autoclean;
9 clearer => 'clear_child_pid',
10 predicate => 'has_child_pid',
13 # XXX: blocking is probably a bad idea; refactor this later
14 requires 'run_parent_loop';
16 my $is_debug = sub { return 1;
17 $_[0]->meta->does_role('MooseX::Runnable::Invocation::Plugin::Debug');
20 sub _restart_parent_setup {
26 return unless $self->has_child_pid;
27 eval { $self->_debug_message("Restarting...") };
28 kill 'HUP', $self->child_pid;
33 return unless $self->has_child_pid;
34 eval { $self->_debug_message("Killing ", $self->child_pid) };
36 kill 'KILL', $self->child_pid;
37 $self->clear_child_pid;
41 my ($next, $self, @args) = @_;
44 local $SIG{CHLD} = sub {
45 # handle the case where the child dies unexpectedly
46 waitpid $self->child_pid, 0;
47 $self->clear_child_pid;
48 my ($code, $sig) = ($? >> 8, $? & 127);
49 eval { $self->_debug_message(
50 "Exiting early, child died with status $code (signal $sig).",
53 # relay the error up, so the shell (etc.) can see it
54 kill $sig, $$ if $sig; # no-op?
59 $self->child_pid( $pid );
60 $self->_restart_parent_setup;
62 my $code = $self->run_parent_loop;
63 eval { $self->_debug_message("Shutting down.") };
69 # we go to all this effort so that the child process is always
70 # free of any "infection" by the parent (like the event loop,
71 # used by the parent to receive filesystem events or signals,
72 # which can't be cancelled by the child)
74 my $child_body; $child_body = sub {
78 # parent? wait for kid to die
79 local $SIG{HUP} = sub {
88 eval { $self->_debug_message(
89 "Child exited with non-zero status; aborting.",
95 # child? actually do the work
96 exit $self->$next(@args);