File Coverage

blib/lib/Feersum/Runner.pm
Criterion Covered Total %
statement 121 131 92.3
branch 26 50 52.0
condition 11 18 61.1
subroutine 24 25 96.0
pod 4 4 100.0
total 186 228 81.5


line stmt bran cond sub pod time code
1             package Feersum::Runner;
2 10     10   2778 use warnings;
  10         34  
  10         413  
3 10     10   82 use strict;
  10         53  
  10         304  
4              
5 10     10   5077 use EV;
  10         22497  
  10         387  
6 10     10   4342 use Feersum;
  10         42  
  10         461  
7 10     10   84 use Socket qw/SOMAXCONN/;
  10         22  
  10         988  
8 10     10   2971 use POSIX ();
  10         37326  
  10         398  
9 10     10   65 use Scalar::Util qw/weaken/;
  10         28  
  10         760  
10 10     10   71 use Carp qw/carp croak/;
  10         21  
  10         532  
11              
12 10     10   57 use constant DEATH_TIMER => 5.0; # seconds
  10         18  
  10         1122  
13 10     10   56 use constant DEATH_TIMER_INCR => 2.0; # seconds
  10         26  
  10         469  
14 10     10   64 use constant DEFAULT_HOST => 'localhost';
  10         31  
  10         498  
15 10     10   57 use constant DEFAULT_PORT => 5000;
  10         19  
  10         13191  
16              
17             our $INSTANCE;
18             sub new { ## no critic (RequireArgUnpacking)
19 12     12 1 104607 my $c = shift;
20             croak "Only one Feersum::Runner instance can be active at a time"
21 12 50 66     242 if $INSTANCE && $INSTANCE->{running};
22 12         375 $INSTANCE = bless {quiet=>1, @_, running=>0}, $c;
23 12         147 return $INSTANCE;
24             }
25              
26             sub DESTROY {
27 3     3   7 local $@;
28 3         7 my $self = shift;
29 3 50       9 if (my $f = $self->{endjinn}) {
30 0     0   0 $f->request_handler(sub{});
31 0         0 $f->unlisten();
32             }
33 3         14 $self->{_quit} = undef;
34 3         23 return;
35             }
36              
37             sub _prepare {
38 8     8   29 my $self = shift;
39              
40             $self->{listen} ||=
41 8   50     157 [ ($self->{host}||DEFAULT_HOST).':'.($self->{port}||DEFAULT_PORT) ];
      50        
      100        
42             croak "Feersum doesn't support multiple 'listen' directives yet"
43 8 50       15 if @{$self->{listen}} > 1;
  8         57  
44 8         45 my $listen = shift @{$self->{listen}};
  8         84  
45              
46 8         36 my $sock;
47 8 50       142 if ($listen =~ m#^unix/#) {
48 0         0 croak "listening on a unix socket isn't supported yet";
49             }
50             else {
51 8         184 require IO::Socket::INET;
52 8         299 $sock = IO::Socket::INET->new(
53             LocalAddr => $listen,
54             ReuseAddr => 1,
55             Proto => 'tcp',
56             Listen => SOMAXCONN,
57             Blocking => 0,
58             );
59 8 50       8128 croak "couldn't bind to socket: $!" unless $sock;
60             }
61 8         57 $self->{sock} = $sock;
62 8         194 my $f = Feersum->endjinn;
63 8         189 $f->use_socket($sock);
64              
65 8 50       115 if ($self->{options}) {
66             # Plack::Runner puts these here
67 0         0 $self->{pre_fork} = delete $self->{options}{pre_fork};
68             }
69              
70 8         99 $self->{endjinn} = $f;
71 8         28 return;
72             }
73              
74             # for overriding:
75             sub assign_request_handler { ## no critic (RequireArgUnpacking)
76 3     3 1 90 return $_[0]->{endjinn}->request_handler($_[1]);
77             }
78              
79             sub run {
80 8     8 1 574 my $self = shift;
81 8         173 weaken $self;
82              
83 8 50       192 $self->{quiet} or warn "Feersum [$$]: starting...\n";
84 8         134 $self->_prepare();
85              
86 8   66     76 my $app = shift || delete $self->{app};
87              
88 8 50 66     79 if (!$app && $self->{app_file}) {
89 3         135 local ($@, $!);
90 3         3579 $app = do $self->{app_file};
91 3 50       15 warn "couldn't parse $self->{app_file}: $@" if $@;
92 3 50 33     21 warn "couldn't do $self->{app_file}: $!" if ($! && !defined $app);
93 3 50       21 warn "couldn't run $self->{app_file}: didn't return anything"
94             unless $app;
95             }
96 8 50       28 die "app not defined or failed to compile" unless $app;
97              
98 8         40 $self->assign_request_handler($app);
99 8         25 undef $app;
100              
101 8     4   190 $self->{_quit} = EV::signal 'QUIT', sub { $self->quit };
  4         114  
102              
103 8 100       69 $self->_start_pre_fork if $self->{pre_fork};
104 6         83761 EV::run;
105 0 0       0 $self->{quiet} or warn "Feersum [$$]: done\n";
106 0         0 $self->DESTROY();
107 0         0 return;
108             }
109              
110             sub _fork_another {
111 5     5   29 my ($self, $slot) = @_;
112 5         33 weaken $self;
113              
114 5         4746 my $pid = fork;
115 5 50       256 croak "failed to fork: $!" unless defined $pid;
116 5 100       92 unless ($pid) {
117 2         138 EV::default_loop()->loop_fork;
118 2 50       43 $self->{quiet} or warn "Feersum [$$]: starting\n";
119 2         139 delete $self->{_kids};
120 2         55 delete $self->{pre_fork};
121 2         38 eval { EV::run; }; ## no critic (RequireCheckingReturnValueOfEval)
  2         48389  
122 0 0       0 carp $@ if $@;
123 0 0       0 POSIX::exit($@ ? -1 : 0); ## no critic (ProhibitMagicNumbers)
124             }
125              
126 3         7 $self->{_n_kids}++;
127             $self->{_kids}[$slot] = EV::child $pid, 0, sub {
128 1     1   12 my $w = shift;
129 1 50       58 $self->{quiet} or warn "Feersum [$$]: child $pid exited ".
130             "with rstatus ".$w->rstatus."\n";
131 1         6 $self->{_n_kids}--;
132 1 50       23 if ($self->{_shutdown}) {
133 1 50       14 EV::break(EV::BREAK_ALL()) unless $self->{_n_kids};
134 1         6698024 return;
135             }
136 0         0 $self->_fork_another();
137 3         318 };
138 3         116 return;
139             }
140              
141             sub _start_pre_fork {
142 3     3   282 my $self = shift;
143              
144 3         162 POSIX::setsid();
145              
146 3         15 $self->{_kids} = [];
147 3         9 $self->{_n_kids} = 0;
148 3         21 $self->_fork_another($_) for (1 .. $self->{pre_fork});
149              
150 1         58 $self->{endjinn}->unlisten();
151 1         9 return;
152             }
153              
154             sub quit {
155 9     9 1 44 my $self = shift;
156 9 100       308058 return if $self->{_shutdown};
157              
158 8         71 $self->{_shutdown} = 1;
159 8 50       122 $self->{quiet} or warn "Feersum [$$]: shutting down...\n";
160 8         31 my $death = DEATH_TIMER;
161              
162 8 100       48 if ($self->{_n_kids}) {
163             # in parent, broadcast SIGQUIT to the group (not self)
164 2         117 kill 3, -$$; ## no critic (ProhibitMagicNumbers)
165 2         35 $death += DEATH_TIMER_INCR;
166             }
167             else {
168             # in child or solo process
169 6     6   186 $self->{endjinn}->graceful_shutdown(sub { POSIX::exit(0) });
  6         297  
170             }
171              
172 2     2   72 $self->{_death} = EV::timer $death, 0, sub { POSIX::exit(1) };
  2         184  
173 2         7006149 return;
174             }
175              
176             1;
177             __END__