File Coverage

blib/lib/Mojo/IOLoop/ReadWriteFork/SIGCHLD.pm
Criterion Covered Total %
statement 34 35 97.1
branch 5 6 83.3
condition 7 17 41.1
subroutine 9 10 90.0
pod 3 3 100.0
total 58 71 81.6


line stmt bran cond sub pod time code
1             package Mojo::IOLoop::ReadWriteFork::SIGCHLD;
2 26     26   140 use Mojo::Base -base;
  26         49  
  26         191  
3              
4 26     26   3993 use POSIX ':sys_wait_h';
  26         50  
  26         226  
5 26     26   3830 use Scalar::Util qw(weaken);
  26         47  
  26         1235  
6              
7 26   50 26   2763 use constant WAIT_PID_INTERVAL => $ENV{WAIT_PID_INTERVAL} || 0.05;
  26         62  
  26         29413  
8              
9             has pids => sub { +{} };
10              
11             sub is_waiting {
12 2     2 1 384564 my $self = shift;
13 2   33     8 return !!(%{$self->pids} || $self->{tid});
14             }
15              
16 27     27 1 1664 sub singleton { state $singleton = Mojo::IOLoop::ReadWriteFork::SIGCHLD->new }
17              
18             sub waitpid {
19 36     36 1 158 my ($self, $pid, $cb) = @_;
20 36         85 push @{$self->pids->{$pid}}, $cb;
  36         188  
21              
22             # The CHLD test is for code, such as Minion::Command::minion::worker
23             # where SIGCHLD is set up for manual waitpid() checks.
24             # See https://github.com/kraih/minion/issues/15 and
25             # https://github.com/jhthorsen/mojo-ioloop-readwritefork/issues/9 for details.
26 36         558 my $reactor = Mojo::IOLoop->singleton->reactor;
27 0     0   0 return $self->{ev}{$pid} ||= EV::child($pid, 0, sub { $self->_exit($pid, shift->rstatus) })
28 36 50 0     2253 if !$SIG{CHLD} and $reactor->isa('Mojo::Reactor::EV');
      33        
29              
30 36         243 weaken $self;
31             $self->{tid} ||= Mojo::IOLoop->recurring(
32             WAIT_PID_INTERVAL,
33             sub {
34 63     63   2843804 my $ioloop = shift;
35 63         495 my $pids = $self->pids;
36 63 100       790 return $ioloop->remove(delete $self->{tid}) unless %$pids;
37              
38 62         267 for my $pid (keys %$pids) {
39 62         1450 local ($?, $!);
40 62         1479 my $kid = CORE::waitpid($pid, WNOHANG);
41 62 100 66     836 $self->_exit($pid, $?) if $kid == $pid or $kid == -1;
42             }
43             }
44 36   66     599 );
45             }
46              
47             sub _exit {
48 35     35   302 my ($self, $pid, $status) = @_;
49 35         174 delete $self->{ev}{$pid};
50 35         193 my $listeners = delete $self->pids->{$pid};
51 35         412 for my $cb (@$listeners) { $cb->($status, $pid) }
  35         213  
52             }
53              
54             1;
55              
56             =head1 NAME
57              
58             Mojo::IOLoop::ReadWriteFork::SIGCHLD - Non-blocking waitpid for Mojolicious
59              
60             =head1 DESCRIPTION
61              
62             L is a module that can wait for a child
63             process to exit. This is currently done either with L or a recurring
64             timer and C.
65              
66             =head1 ATTRIBUTES
67              
68             $hash_ref = $sigchld->pids;
69              
70             Returns a hash ref where the keys are active child process IDs and the values
71             are array-refs of callbacks passed on to L.
72              
73             =head1 METHODS
74              
75             =head2 is_waiting
76              
77             $bool = $sigchld->is_waiting;
78              
79             Returns true if C<$sigchld> is still has a recurring timer or waiting for a
80             process to exit.
81              
82             =head2 singleton
83              
84             $sigchld = Mojo::IOLoop::ReadWriteFork::SIGCHLD->singleton;
85              
86             Returns a shared L object.
87              
88             =head2 waitpid
89              
90             $sigchld->waitpid($pid, sub { my ($exit_value) = @_ });
91              
92             Will call the provided callback with C<$?> when the C<$pid> is no longer running.
93              
94             =head1 SEE ALSO
95              
96             L.
97              
98             =cut