File Coverage

blib/lib/Plack/Handler/Starlight.pm
Criterion Covered Total %
statement 89 103 86.4
branch 10 18 55.5
condition 3 6 50.0
subroutine 15 16 93.7
pod 0 2 0.0
total 117 145 80.6


line stmt bran cond sub pod time code
1             package Plack::Handler::Starlight;
2              
3             =head1 NAME
4              
5             Plack::Handler::Starlight - Plack adapter for Starlight
6              
7             =head1 SYNOPSIS
8              
9             =for markdown ```perl
10              
11             use Plack::Loader;
12              
13             my $loader = Plack::Loader->load('Starlight', port => 80);
14             $loader->run(sub { [200, ['Content-Type', 'text/plain'], ['PSGI app']] });
15              
16             =for markdown ```
17              
18             =head1 DESCRIPTION
19              
20             This is a stub module that allows Starlight to be loaded up under L
21             and other L tools. Set C<$ENV{PLACK_SERVER}> to C<'Starlight'> or use
22             the -s parameter to L to use Starlight under L.
23              
24             See L and L (lower case) for available command line
25             options.
26              
27             =cut
28              
29 87     87   1714559 use strict;
  87         950  
  87         7353  
30 87     87   1387 use warnings;
  87         477  
  87         14806  
31              
32             our $VERSION = '0.0503';
33              
34 87     87   1469 use base qw(Starlight::Server);
  87         603  
  87         70938  
35              
36 87     87   691 use Config ();
  87         191  
  87         1792  
37 87     87   453 use English '-no_match_vars';
  87         230  
  87         762  
38 87     87   34819 use Fcntl ();
  87         196  
  87         1508  
39 87     87   477 use File::Spec;
  87         174  
  87         2956  
40 87     87   573 use POSIX ();
  87         179  
  87         2041  
41 87     87   531 use Plack::Util;
  87         349  
  87         6021  
42              
43 87   50 87   853 use constant HAS_WIN32_PROCESS => $^O eq 'cygwin' && eval { require Win32::Process; 1; } && 1;
  87         173  
  87         7215  
44              
45 87     87   584 use constant DEBUG => $ENV{PERL_STARLIGHT_DEBUG};
  87         192  
  87         95774  
46              
47             sub new {
48 86     86 0 4945 my ($class, %args) = @_;
49              
50             # setup before instantiation
51 86         234 my $max_workers = 10;
52 86         285 for (qw(max_workers workers)) {
53             $max_workers = delete $args{$_}
54 172 100       704 if defined $args{$_};
55             }
56              
57             # instantiate and set the variables
58 86         835 my $self = $class->SUPER::new(%args);
59 86 50       500 if ($^O eq 'MSWin32') {
60              
61             # forks are emulated
62 0         0 $self->{is_multithread} = Plack::Util::TRUE;
63 0         0 $self->{is_multiprocess} = Plack::Util::FALSE;
64             } else {
65              
66             # real forks
67 86         593 $self->{is_multithread} = Plack::Util::FALSE;
68 86         229 $self->{is_multiprocess} = Plack::Util::TRUE;
69             }
70 86         178 $self->{max_workers} = $max_workers;
71              
72 86         1426 $self->{main_process} = $$;
73 86         394 $self->{processes} = +{};
74              
75 86         307 $self->{_kill_stalled_processes_delay} = 10;
76              
77 86         1081 $self;
78             }
79              
80             sub run {
81 85     85 0 4043 my ($self, $app) = @_;
82              
83 85         1653 $self->_daemonize();
84              
85 85         136 warn "*** starting main process $$" if DEBUG;
86 85         935 $self->setup_listener();
87              
88 85         1266 $self->_setup_privileges();
89              
90 85         2420 local $SIG{PIPE} = 'IGNORE';
91              
92             local $SIG{CHLD} = sub {
93 356     356   2284 my ($sig) = @_;
94 356         599 warn "*** SIG$sig received in process $$" if DEBUG;
95 356         14090 local ($!, $?);
96 356         14367 my $pid = waitpid(-1, &POSIX::WNOHANG); ## no critic
97 356 100       2045 return if $pid == -1;
98 349         207001 delete $self->{processes}->{$pid};
99 85         1974 };
100              
101 85         300 my $sigint = $self->{_sigint};
102 85 50       452 my $sigterm = $^O eq 'MSWin32' ? 'KILL' : 'TERM';
103              
104 85 50       381 if ($self->{max_workers} != 0) {
105             local $SIG{$sigint} = local $SIG{TERM} = sub {
106 7     7   327 my ($sig) = @_;
107 7         130 warn "*** SIG$sig received in process $$" if DEBUG;
108 7         373 $self->{term_received}++;
109 85         2777 };
110 85         531 for (my $loop = 0; not $self->{term_received}; $loop++) {
111 926         5925 warn "*** running ", scalar keys %{ $self->{processes} }, " processes" if DEBUG;
112 926 50 50     8922 if ($loop >= $self->{_kill_stalled_processes_delay} / ($self->{main_process_delay} || 1)) {
113 0         0 $loop = 0;
114              
115             # check stalled processes once per n sec
116 0         0 foreach my $pid (keys %{ $self->{processes} }) {
  0         0  
117 0 0       0 delete $self->{processes}->{$pid} if not kill 0, $pid;
118             }
119             }
120 926         2380 foreach my $n (1 + scalar keys %{ $self->{processes} } .. $self->{max_workers}) {
  926         6942  
121 849         4252 $self->_create_process($app);
122 771         14500 $self->_sleep($self->{spawn_interval});
123             }
124              
125             # slow down main process
126 848         10902 $self->_sleep($self->{main_process_delay});
127             }
128 7 50       172 if (my @pids = keys %{ $self->{processes} }) {
  7         350  
129 7         119 warn "*** stopping ", scalar @pids, " processes" if DEBUG;
130 7         153 foreach my $pid (@pids) {
131 55         305 warn "*** stopping process $pid" if DEBUG;
132 55         2709 kill $sigterm, $pid;
133             }
134 7         86 if (HAS_WIN32_PROCESS) {
135             $self->_sleep(1);
136             foreach my $pid (keys %{ $self->{processes} }) {
137             my $winpid = Cygwin::pid_to_winpid($pid) or next;
138             warn "*** terminating process $pid winpid $winpid" if DEBUG;
139             Win32::Process::KillProcess($winpid, 0);
140             }
141             }
142 7         215 $self->_sleep(1);
143 7         222 foreach my $pid (keys %{ $self->{processes} }) {
  7         92  
144 48         184 warn "*** waiting for process ", $pid if DEBUG;
145 48         68697 waitpid $pid, 0;
146             }
147             }
148 7 50 50     367 if ($^O eq 'cygwin' and not HAS_WIN32_PROCESS) {
149 0         0 warn "Win32::Process is not installed. Some processes might be still active.\n";
150             }
151 7         83 warn "*** stopping main process $$" if DEBUG;
152 7         1376 exit 0;
153             } else {
154              
155             # run directly, mainly for debugging
156             local $SIG{$sigint} = local $SIG{TERM} = sub {
157 0     0     my ($sig) = @_;
158 0           warn "*** SIG$sig received in process $$" if DEBUG;
159 0           exit 0;
160 0           };
161 0           while (1) {
162 0           $self->accept_loop($app, $self->_calc_reqs_per_child());
163 0           $self->_sleep($self->{spawn_interval});
164             }
165             }
166             }
167              
168             1;
169              
170             __END__