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 97     97   116532034 use strict;
  97         1708  
  97         11721  
30 97     97   1970 use warnings;
  97         849  
  97         23255  
31              
32             our $VERSION = '0.0501';
33              
34 97     97   2484 use base qw(Starlight::Server);
  97         513  
  97         97091  
35              
36 97     97   849 use Config ();
  97         272  
  97         2036  
37 97     97   498 use English '-no_match_vars';
  97         304  
  97         867  
38 97     97   38381 use Fcntl ();
  97         497  
  97         1719  
39 97     97   524 use File::Spec;
  97         191  
  97         2494  
40 97     97   669 use POSIX ();
  97         193  
  97         1877  
41 97     97   586 use Plack::Util;
  97         260  
  97         8400  
42              
43 97   50 97   721 use constant HAS_WIN32_PROCESS => $^O eq 'cygwin' && eval { require Win32::Process; 1; } && 1;
  97         210  
  97         7949  
44              
45 97     97   1040 use constant DEBUG => $ENV{PERL_STARLIGHT_DEBUG};
  97         518  
  97         106366  
46              
47             sub new {
48 96     96 0 5985 my ($class, %args) = @_;
49              
50             # setup before instantiation
51 96         214 my $max_workers = 10;
52 96         264 for (qw(max_workers workers)) {
53             $max_workers = delete $args{$_}
54 192 100       1091 if defined $args{$_};
55             }
56              
57             # instantiate and set the variables
58 96         1158 my $self = $class->SUPER::new(%args);
59 96 50       407 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 96         751 $self->{is_multithread} = Plack::Util::FALSE;
68 96         231 $self->{is_multiprocess} = Plack::Util::TRUE;
69             }
70 96         181 $self->{max_workers} = $max_workers;
71              
72 96         1339 $self->{main_process} = $$;
73 96         475 $self->{processes} = +{};
74              
75 96         210 $self->{_kill_stalled_processes_delay} = 10;
76              
77 96         983 $self;
78             }
79              
80             sub run {
81 95     95 0 5287 my ($self, $app) = @_;
82              
83 95         2166 $self->_daemonize();
84              
85 95         139 warn "*** starting main process $$" if DEBUG;
86 95         892 $self->setup_listener();
87              
88 95         1218 $self->_setup_privileges();
89              
90 95         2822 local $SIG{PIPE} = 'IGNORE';
91              
92             local $SIG{CHLD} = sub {
93 343     343   3758 my ($sig) = @_;
94 343         731 warn "*** SIG$sig received in process $$" if DEBUG;
95 343         15310 local ($!, $?);
96 343         13701 my $pid = waitpid(-1, &POSIX::WNOHANG); ## no critic
97 343 100       1997 return if $pid == -1;
98 336         343310 delete $self->{processes}->{$pid};
99 95         2230 };
100              
101 95         352 my $sigint = $self->{_sigint};
102 95 50       1097 my $sigterm = $^O eq 'MSWin32' ? 'KILL' : 'TERM';
103              
104 95 50       460 if ($self->{max_workers} != 0) {
105             local $SIG{$sigint} = local $SIG{TERM} = sub {
106 8     8   376 my ($sig) = @_;
107 8         128 warn "*** SIG$sig received in process $$" if DEBUG;
108 8         439 $self->{term_received}++;
109 95         3145 };
110 95         542 for (my $loop = 0; not $self->{term_received}; $loop++) {
111 893         8250 warn "*** running ", scalar keys %{ $self->{processes} }, " processes" if DEBUG;
112 893 50 50     8932 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 893         2730 foreach my $n (1 + scalar keys %{ $self->{processes} } .. $self->{max_workers}) {
  893         9277  
121 880         5116 $self->_create_process($app);
122 793         16010 $self->_sleep($self->{spawn_interval});
123             }
124              
125             # slow down main process
126 806         12038 $self->_sleep($self->{main_process_delay});
127             }
128 8 50       237 if (my @pids = keys %{ $self->{processes} }) {
  8         410  
129 8         91 warn "*** stopping ", scalar @pids, " processes" if DEBUG;
130 8         131 foreach my $pid (@pids) {
131 65         319 warn "*** stopping process $pid" if DEBUG;
132 65         3502 kill $sigterm, $pid;
133             }
134 8         128 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 8         239 $self->_sleep(1);
143 8         295 foreach my $pid (keys %{ $self->{processes} }) {
  8         116  
144 54         159 warn "*** waiting for process ", $pid if DEBUG;
145 54         126915 waitpid $pid, 0;
146             }
147             }
148 8 50 50     414 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 8         76 warn "*** stopping main process $$" if DEBUG;
152 8         1535 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__