File Coverage

blib/lib/Proc/Guard.pm
Criterion Covered Total %
statement 24 66 36.3
branch 0 26 0.0
condition 0 17 0.0
subroutine 8 13 61.5
pod 4 4 100.0
total 36 126 28.5


line stmt bran cond sub pod time code
1             package Proc::Guard;
2 1     1   12971 use strict;
  1         1  
  1         25  
3 1     1   4 use warnings;
  1         1  
  1         18  
4 1     1   12 use 5.00800;
  1         4  
  1         39  
5             our $VERSION = '0.07';
6 1     1   3 use Carp ();
  1         0  
  1         28  
7              
8             our $EXIT_STATUS;
9              
10             # functional interface
11             our @EXPORT = qw/proc_guard/;
12 1     1   3 use Exporter 'import';
  1         1  
  1         101  
13             sub proc_guard {
14 0     0 1   return Proc::Guard->new(do {
15 0 0 0       if (@_==1 && ref($_[0]) && ref($_[0]) eq 'CODE') {
      0        
16 0           +{ code => $_[0] }
17             } else {
18 0           +{ command => [@_] }
19             }
20             });
21             }
22              
23             # OOish interface
24 1     1   384 use POSIX;
  1         4863  
  1         3  
25 1     1   2095 use Errno qw/EINTR ECHILD/;
  1         822  
  1         153  
26             use Class::Accessor::Lite 0.05 (
27 1         5 rw => ['pid'],
28 1     1   497 );
  1         724  
29              
30             sub new {
31 0     0 1   my $class = shift;
32 0 0         my %args = @_==1 ? %{$_[0]} : @_;
  0            
33              
34 0           my $self = bless {
35             _owner_pid => $$,
36             auto_start => 1,
37             %args,
38             }, $class;
39              
40 0 0 0       if ($self->{command} && !ref($self->{command})) {
41 0           $self->{command} = [$self->{command}];
42             }
43 0 0 0       unless ($self->{command} || $self->{code}) {
44 0           Carp::croak("'command' or 'code' is required.");
45             }
46              
47             $self->start()
48 0 0         if $self->{auto_start};
49              
50 0           return $self;
51             }
52              
53             sub start {
54 0     0 1   my $self = shift;
55              
56 0           my $pid = fork();
57 0 0         die "fork failed: $!" unless defined $pid;
58 0 0         if ($pid == 0) { # child
59 0 0         if ($self->{command}) {
60 0           exec @{$self->{command}};
  0            
61 0           die "cannot exec @{$self->{command}}: $!";
  0            
62             } else {
63 0           $self->{code}->();
64 0           exit(0); # exit after work
65             }
66             }
67 0           $self->pid($pid);
68             }
69              
70             sub stop {
71 0     0 1   my ( $self, $sig ) = @_;
72             return
73 0 0         unless defined $self->pid;
74 0   0       $sig ||= SIGTERM;
75              
76 0           kill $sig, $self->pid;
77             LOOP: {
78 0 0         if ( waitpid( $self->pid, 0 ) > 0 ) {
  0            
79 0           $EXIT_STATUS = $?;
80 0           last LOOP;
81             }
82              
83 0 0         redo LOOP if $! == EINTR;
84              
85             # on any other error, we have no reason to think that
86             # trying again will succeed; on ECHILD, that pid is gone
87             # or not ours, so give up; anything else is strange
88 0 0         warn "waitpid() error: $!\n" if $! != ECHILD;
89              
90             # waitpid wasn't successful so $? is undefined
91 0           $EXIT_STATUS = undef;
92             }
93              
94 0           $self->pid(undef);
95             }
96              
97             sub DESTROY {
98 0     0     my $self = shift;
99 0 0 0       if (defined $self->pid && $$ == $self->{_owner_pid}) {
100 0           local $?; # "END" function and destructors can change the exit status by modifying $?.(perldoc -f exit)
101 0           $self->stop()
102             }
103             }
104              
105             1;
106             __END__