Commit | Line | Data |
d69f95ab |
1 | package MooseX::Runnable::Invocation::Plugin::Restart::Base; |
2 | use Moose::Role; |
3 | use MooseX::Types::Moose qw(Int); |
4 | use namespace::autoclean; |
5 | |
6 | has 'child_pid' => ( |
7 | is => 'rw', |
8 | isa => Int, |
9 | clearer => 'clear_child_pid', |
10 | predicate => 'has_child_pid', |
11 | ); |
12 | |
13 | requires 'run_parent_loop'; |
14 | |
15 | my $is_debug = sub { return 1; |
16 | $_[0]->meta->does_role('MooseX::Runnable::Invocation::Plugin::Debug'); |
17 | }; |
18 | |
19 | sub _restart_parent_setup { |
20 | my $self = shift; |
21 | } |
22 | |
23 | sub restart { |
24 | my $self = shift; |
25 | return unless $self->has_child_pid; |
26 | eval { $self->_debug_message("Restarting...") }; |
27 | kill 'HUP', $self->child_pid; |
28 | } |
29 | |
30 | sub kill_child { |
31 | my $self = shift; |
32 | return unless $self->has_child_pid; |
33 | eval { $self->_debug_message("Killing ", $self->child_pid) }; |
34 | |
35 | kill 'KILL', $self->child_pid; |
36 | $self->clear_child_pid; |
37 | } |
38 | |
39 | around 'run' => sub { |
40 | my ($next, $self, @args) = @_; |
41 | my $pid = fork(); |
42 | if($pid){ |
c15146cc |
43 | local $SIG{CHLD} = sub { |
44 | # handle the case where the child dies unexpectedly |
45 | waitpid $self->child_pid, 0; |
46 | $self->clear_child_pid; |
7bd2975e |
47 | my ($code, $sig) = ($? >> 8, $? & 127); |
48 | eval { $self->_debug_message( |
49 | "Exiting early, child died with status $code (signal $sig).", |
50 | )}; |
51 | |
52 | # relay the error up, so the shell (etc.) can see it |
53 | kill $sig, $$ if $sig; # no-op? |
54 | exit $code; |
c15146cc |
55 | }; |
d69f95ab |
56 | |
57 | # parent |
58 | $self->child_pid( $pid ); |
59 | $self->_restart_parent_setup; |
60 | |
61 | my $code = $self->run_parent_loop; |
62 | eval { $self->_debug_message("Shutting down.") }; |
63 | |
64 | $self->kill_child; |
65 | return $code; |
66 | } |
67 | else { |
68 | # we go to all this effort so that the child process is always |
69 | # free of any "infection" by the parent (like the event loop, |
70 | # used by the parent to receive filesystem events or signals, |
71 | # which can't be cancelled by the child) |
72 | |
73 | my $child_body; $child_body = sub { |
74 | while(1){ |
75 | my $pid2 = fork; |
76 | if($pid2){ |
77 | # parent? wait for kid to die |
78 | local $SIG{HUP} = sub { |
79 | kill 'KILL', $pid2; |
80 | }; |
81 | waitpid $pid2, 0; |
7bd2975e |
82 | my $code = $? >> 8; |
83 | if($code == 0){ |
84 | goto $child_body; |
85 | } |
86 | else { |
87 | eval { $self->_debug_message( |
88 | "Child exited with non-zero status; aborting.", |
89 | )}; |
90 | exit $code; |
91 | } |
d69f95ab |
92 | } |
93 | else { |
94 | # child? actually do the work |
95 | exit $self->$next(@args); |
d69f95ab |
96 | } |
97 | } |
98 | }; |
99 | |
100 | $child_body->(); |
101 | } |
102 | }; |
103 | |
104 | 1; |