File Coverage

blib/lib/Proc/Supervised/Convenience.pm
Criterion Covered Total %
statement 5 7 71.4
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 8 10 80.0


line stmt bran cond sub pod time code
1             package Proc::Supervised::Convenience;
2             BEGIN {
3 1     1   895 $Proc::Supervised::Convenience::VERSION = '1.01';
4             }
5              
6             =head1 NAME
7              
8             Proc::Supervised::Convenience - Supervise concurrent worker processes
9              
10             =head1 VERSION
11              
12             version 1.01
13              
14             =cut
15              
16 1     1   148879 use POE;
  1         128794  
  1         8  
17 1     1   120251 use POE::Component::Supervisor;
  0            
  0            
18             use POE::Component::Supervisor::Supervised::Proc;
19             use POSIX;
20             use Moose;
21             with 'MooseX::Getopt';
22              
23             has processes => (
24             is => 'ro',
25             isa => 'Int',
26             cmd_aliases => ['j'],
27             traits => ['Getopt']
28             );
29             has detach => (
30             is => 'ro',
31             isa => 'Bool',
32             default => 0,
33             cmd_aliases => ['d'],
34             traits => ['Getopt']
35             );
36             has program => (
37             is => 'ro',
38             isa => 'CodeRef',
39             traits => ['NoGetopt'],
40             required => 1
41             );
42             has logger => (
43             is => 'ro',
44             isa => 'Log::Dispatch',
45             traits => ['NoGetopt'],
46             required => 0
47             );
48              
49             sub detach_me {
50             my $self = shift;
51             $self->logger->info("Detaching $$") if $self->logger;
52              
53             local $SIG{HUP} = sub {
54             $self->logger->debug("Got sighup in $$.") if $self->logger;
55             };
56             my $child = fork();
57             $child >= 0 or die "Fork failed ($!)";
58             $child == 0 or exit 0;
59              
60             POSIX::setsid;
61             # close std file descriptors
62             if (-e "/dev/null") {
63             # On Unix, we want to point these file descriptors at /dev/null,
64             # so that any libary routines that try to read form stdin or
65             # write to stdout/err will have no effect (Stevens, APitUE, p. 426
66             # and [RT 51066].
67             open STDIN, '/dev/null';
68             open STDOUT, '>>/dev/null';
69             open STDERR, '>>/dev/null';
70             } else {
71             close(STDIN);
72             close(STDOUT);
73             close(STDERR);
74             }
75             }
76              
77             sub make_children {
78             my $self = shift;
79              
80             map {
81             POE::Component::Supervisor::Supervised::Proc->new(
82             restart_policy => 'permanent',
83             until_kill => 2,
84             until_term => 1,
85             program => sub {
86             local $SIG{HUP} = 'IGNORE'; # so we can killall -HUP
87             $self->program->(@{ $self->extra_argv });
88             },
89             ) } 1 .. $self->processes
90             }
91              
92             sub supervise {
93             my $self = shift;
94             $self->detach_me() if $self->detach;
95              
96             my $supervisor;
97             POE::Session->create(
98             inline_states => {
99             _start => sub {
100             $_[KERNEL]->sig(INT => 'kill_all');
101             $_[KERNEL]->sig(HUP => 'restart_all');
102             $_[KERNEL]->sig(USR1 => 'relaunch');
103              
104             $supervisor = POE::Component::Supervisor->new(
105             children => [ $self->make_children() ],
106             restart_policy => 'one',
107             until_kill => 0.2,
108             ($self->logger ? (logger => $self->logger) : ())
109             );
110             },
111             kill_all => sub { $supervisor->logger->info(" *** Stopping all *** ");
112             $supervisor->stop;
113             $_[KERNEL]->sig_handled
114             },
115             restart_all => sub { $supervisor->logger->info( " *** Restarting all *** ");
116             $supervisor->stop;
117             $_[KERNEL]->sig_handled;
118             $supervisor->start($self->make_children());
119             },
120             relaunch => sub { $supervisor->logger->info( " *** Relaunching *** ");
121             $supervisor->stop;
122             $_[KERNEL]->sig_handled;
123             exec ($0, @{ $self->ARGV });
124             },
125             }
126             );
127              
128             POE::Kernel->run();
129             }
130              
131             1;
132              
133             __END__
134              
135             =head1 SYNOPSIS
136              
137             driver script:
138              
139             #!/usr/bin/perl
140              
141             use Proc::Supervised::Convenience;
142              
143             Proc::Supervised::Convenience
144             ->new_with_options( program => \&work )
145             ->supervise;
146              
147             sub work {
148             my @args = @_;
149             # code to run forever
150             }
151              
152             invocation:
153              
154             ./work -d -j 10 foo bar
155              
156             =head1 FEATURES
157              
158             =over 4
159              
160             =item * auto-restarts worker processes
161              
162             =item * kill -HUP to restart all workers
163              
164             =item * kill -INT to stop
165              
166             =item * kill -USR1 to relaunch
167              
168             =back
169              
170              
171             =head1 Command-line options
172              
173             =over 4
174              
175             =item * --detach | -d # detach from terminal
176              
177             =item * --processes | -j N # run N copies of &work
178              
179             =back
180              
181             Any remaining command line arguments are passed on as is to your work subroutine.
182              
183             =head1 SEE ALSO
184              
185             L<POE::Component::Supervisor>.
186              
187             =head1 COPYRIGHT & LICENSE
188              
189             Copyright 2011 Rhesa Rozendaal, all rights reserved.
190              
191             This program is free software; you can redistribute it and/or modify it
192             under the same terms as Perl itself.
193              
194             =cut