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 23     23   124 use Mojo::Base -base;
  23         42  
  23         142  
3              
4 23     23   3125 use POSIX ':sys_wait_h';
  23         47  
  23         151  
5 23     23   3253 use Scalar::Util qw(weaken);
  23         49  
  23         1074  
6              
7 23   50 23   102 use constant WAIT_PID_INTERVAL => $ENV{WAIT_PID_INTERVAL} || 0.05;
  23         2631  
  23         26325  
8              
9             has pids => sub { +{} };
10              
11             sub is_waiting {
12 2     2 1 386449 my $self = shift;
13 2   33     5 return !!(%{$self->pids} || $self->{tid});
14             }
15              
16 24     24 1 248 sub singleton { state $singleton = Mojo::IOLoop::ReadWriteFork::SIGCHLD->new }
17              
18             sub waitpid {
19 33     33 1 202 my ($self, $pid, $cb) = @_;
20 33         94 push @{$self->pids->{$pid}}, $cb;
  33         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 33         495 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     1788 if !$SIG{CHLD} and $reactor->isa('Mojo::Reactor::EV');
      33        
29              
30 33         230 weaken $self;
31             $self->{tid} ||= Mojo::IOLoop->recurring(
32             WAIT_PID_INTERVAL,
33             sub {
34 57     57   2571515 my $ioloop = shift;
35 57         581 my $pids = $self->pids;
36 57 100       689 return $ioloop->remove(delete $self->{tid}) unless %$pids;
37              
38 56         248 for my $pid (keys %$pids) {
39 56         1174 local ($?, $!);
40 56         4348 my $kid = CORE::waitpid($pid, WNOHANG);
41 56 100 66     877 $self->_exit($pid, $?) if $kid == $pid or $kid == -1;
42             }
43             }
44 33   66     565 );
45             }
46              
47             sub _exit {
48 32     32   198 my ($self, $pid, $status) = @_;
49 32         132 delete $self->{ev}{$pid};
50 32         149 my $listeners = delete $self->pids->{$pid};
51 32         323 for my $cb (@$listeners) { $cb->($status, $pid) }
  32         277  
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