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 22     22   143 use Mojo::Base -base;
  22         46  
  22         150  
3              
4 22     22   3614 use POSIX ':sys_wait_h';
  22         55  
  22         166  
5 22     22   3424 use Scalar::Util qw(weaken);
  22         71  
  22         1262  
6              
7 22   50 22   136 use constant WAIT_PID_INTERVAL => $ENV{WAIT_PID_INTERVAL} || 0.05;
  22         62  
  22         29472  
8              
9             has pids => sub { +{} };
10              
11             sub is_waiting {
12 2     2 1 396934 my $self = shift;
13 2   33     6 return !!(%{$self->pids} || $self->{tid});
14             }
15              
16 23     23 1 270 sub singleton { state $singleton = Mojo::IOLoop::ReadWriteFork::SIGCHLD->new }
17              
18             sub waitpid {
19 33     33 1 326 my ($self, $pid, $cb) = @_;
20 33         263 push @{$self->pids->{$pid}}, $cb;
  33         532  
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 33         771 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 33 50 0     2418 if !$SIG{CHLD} and $reactor->isa('Mojo::Reactor::EV');
      33        
29              
30 33         448 weaken $self;
31             $self->{tid} ||= Mojo::IOLoop->recurring(
32             WAIT_PID_INTERVAL,
33             sub {
34 56     56   2430068 my $ioloop = shift;
35 56         639 my $pids = $self->pids;
36 56 100       842 return $ioloop->remove(delete $self->{tid}) unless %$pids;
37              
38 55         317 for my $pid (keys %$pids) {
39 55         1732 local ($?, $!);
40 55         1875 my $kid = CORE::waitpid($pid, WNOHANG);
41 55 100 66     1194 $self->_exit($pid, $?) if $kid == $pid or $kid == -1;
42             }
43             }
44 33   66     845 );
45             }
46              
47             sub _exit {
48 32     32   391 my ($self, $pid, $status) = @_;
49 32         227 delete $self->{ev}{$pid};
50 32         268 my $listeners = delete $self->pids->{$pid};
51 32         570 for my $cb (@$listeners) { $cb->($status, $pid) }
  32         465  
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