File Coverage

blib/lib/POE/Resource/Signals.pm
Criterion Covered Total %
statement 289 324 89.2
branch 92 126 73.0
condition 53 66 80.3
subroutine 47 47 100.0
pod n/a
total 481 563 85.4


line stmt bran cond sub pod time code
1             # The data necessary to manage signals, and the accessors to get at
2             # that data in a sane fashion.
3              
4             package POE::Resource::Signals;
5              
6 175     175   890 use vars qw($VERSION);
  175         266  
  175         9271  
7             $VERSION = '1.365'; # NOTE - Should be #.### (three decimal places)
8              
9             # These methods are folded into POE::Kernel;
10             package POE::Kernel;
11              
12 175     175   787 use strict;
  175         283  
  175         4700  
13              
14 175     175   792 use POE::Pipe::OneWay;
  175         242  
  175         3340  
15 175     175   676 use POE::Resource::FileHandles;
  175         235  
  175         3909  
16 175     175   740 use POSIX qw(:sys_wait_h sigprocmask SIG_SETMASK);
  175         214  
  175         1509  
17              
18             ### Map watched signal names to the sessions that are watching them
19             ### and the events that must be delivered when they occur.
20              
21             sub SEV_EVENT () { 0 }
22             sub SEV_ARGS () { 1 }
23             sub SEV_SESSION () { 2 }
24              
25             my %kr_signals;
26             # ( $signal_name =>
27             # { $session_id =>
28             # [ $event_name, SEV_EVENT
29             # $event_args, SEV_ARGS
30             # $session_ref, SEV_SESSION
31             # ],
32             # ...,
33             # },
34             # ...,
35             # );
36              
37             my %kr_sessions_to_signals;
38             # ( $session_id =>
39             # { $signal_name =>
40             # [ $event_name, SEV_EVENT
41             # $event_args, SEV_ARGS
42             # $session_ref, SEV_SESSION
43             # ],
44             # ...,
45             # },
46             # ...,
47             # );
48              
49             my %kr_pids_to_events;
50             # { $pid =>
51             # { $session_id =>
52             # [ $blessed_session, # PID_SESSION
53             # $event_name, # PID_EVENT
54             # $args, # PID_ARGS
55             # ]
56             # }
57             # }
58              
59             my %kr_sessions_to_pids;
60             # { $session_id => { $pid => 1 } }
61              
62             sub PID_SESSION () { 0 }
63             sub PID_EVENT () { 1 }
64             sub PID_ARGS () { 2 }
65              
66             sub _data_sig_relocate_kernel_id {
67 4     4   25 my ($self, $old_id, $new_id) = @_;
68              
69 4         113 while (my ($signal, $sig_rec) = each %kr_signals) {
70 4 50       87 next unless exists $sig_rec->{$old_id};
71 4         94 $sig_rec->{$new_id} = delete $sig_rec->{$old_id};
72             }
73              
74 4 50       62 $kr_sessions_to_signals{$new_id} = delete $kr_sessions_to_signals{$old_id}
75             if exists $kr_sessions_to_signals{$old_id};
76              
77 4         51 while (my ($pid, $pid_rec) = each %kr_pids_to_events) {
78 2 50       45 next unless exists $pid_rec->{$old_id};
79 0         0 $pid_rec->{$new_id} = delete $pid_rec->{$old_id};
80             }
81              
82 4 50       46 $kr_sessions_to_pids{$new_id} = delete $kr_sessions_to_pids{$old_id}
83             if exists $kr_sessions_to_pids{$old_id};
84             }
85              
86             # Bookkeeping per dispatched signal.
87              
88             # TODO - Why not lexicals?
89             use vars (
90 175         10832 '@kr_signaled_sessions', # The sessions touched by a signal.
91             '$kr_signal_total_handled', # How many sessions handled a signal.
92             '$kr_signal_type', # The type of signal being dispatched.
93 175     175   76768 );
  175         406  
94              
95             #my @kr_signaled_sessions; # The sessions touched by a signal.
96             #my $kr_signal_total_handled; # How many sessions handled a signal.
97             #my $kr_signal_type; # The type of signal being dispatched.
98              
99             # A flag to tell whether we're currently polling for signals.
100             # Under USE_SIGCHLD, determines whether a SIGCHLD polling event has
101             # already been queued.
102             my $polling_for_signals = 0;
103              
104             # There may be latent subprocesses in some environments.
105             # Or we may need to "always loop once" if we're polling for SIGCHLD.
106             # This constant lets us define those exceptional cases.
107             # We had some in the past, but as of 2013-10-06 we seem to have
108             # eliminated those special cases.
109 175     175   912 use constant BASE_SIGCHLD_COUNT => 0;
  175         316  
  175         483350  
110              
111             my $kr_has_child_procs = BASE_SIGCHLD_COUNT;
112              
113             # A list of special signal types. Signals that aren't listed here are
114             # benign (they do not kill sessions at all). "Terminal" signals are
115             # the ones that UNIX defaults to killing processes with. Thus STOP is
116             # not terminal.
117              
118             sub SIGTYPE_BENIGN () { 0x00 }
119             sub SIGTYPE_TERMINAL () { 0x01 }
120             sub SIGTYPE_NONMASKABLE () { 0x02 }
121              
122             my %_signal_types = (
123             QUIT => SIGTYPE_TERMINAL,
124             INT => SIGTYPE_TERMINAL,
125             KILL => SIGTYPE_TERMINAL,
126             TERM => SIGTYPE_TERMINAL,
127             HUP => SIGTYPE_TERMINAL,
128             IDLE => SIGTYPE_TERMINAL,
129             DIE => SIGTYPE_TERMINAL,
130             ZOMBIE => SIGTYPE_NONMASKABLE,
131             UIDESTROY => SIGTYPE_NONMASKABLE,
132             );
133              
134             # Build a list of useful, real signals. Nonexistent signals, and ones
135             # which are globally unhandled, usually cause segmentation faults if
136             # perl was poorly configured. Some signals aren't available in some
137             # environments.
138              
139             my %_safe_signals;
140              
141             sub _data_sig_initialize {
142 269     269   686 my $self = shift;
143              
144 269         1132 $self->_data_sig_reset_procs;
145              
146 269         531 $poe_kernel->[KR_SIGNALS] = \%kr_signals;
147 269         544 $poe_kernel->[KR_PIDS] = \%kr_pids_to_events;
148              
149             # In case we're called multiple times.
150 269 100       891 unless (keys %_safe_signals) {
151 268         5029 foreach my $signal (keys %SIG) {
152              
153             # Nonexistent signals, and ones which are globally unhandled.
154             next if (
155 18352 100       37399 $signal =~ /^
156             ( NUM\d+
157             |__[A-Z0-9]+__
158             |ALL|CATCHALL|DEFER|HOLD|IGNORE|MAX|PAUSE
159             |RTMIN|RTMAX|SETS
160             |SEGV
161             |
162             )
163             $/x
164             );
165              
166             # Windows doesn't have a SIGBUS, but the debugger causes SIGBUS
167             # to be entered into %SIG. It's fatal to register its handler.
168 9112 50 66     15363 next if $signal eq 'BUS' and RUNNING_IN_HELL;
169              
170             # Apache uses SIGCHLD and/or SIGCLD itself, so we can't.
171 9112 50 66     17382 next if $signal =~ /^CH?LD$/ and exists $INC{'Apache.pm'};
172              
173 9112         12480 $_safe_signals{$signal} = 1;
174             }
175              
176             # Reset some important signal handlers. The rest remain
177             # untouched.
178              
179 268 50       2672 $self->loop_ignore_signal("CHLD") if exists $SIG{CHLD};
180 267 50       1615 $self->loop_ignore_signal("CLD") if exists $SIG{CLD};
181 267 50       1468 $self->loop_ignore_signal("PIPE") if exists $SIG{PIPE};
182              
183 267         936 $self->_data_sig_pipe_build if USE_SIGNAL_PIPE;
184             }
185             }
186              
187             sub _data_sig_has_forked {
188 4     4   12 my( $self ) = @_;
189 4         46 $self->_data_sig_reset_procs;
190 4         11 if( USE_SIGNAL_PIPE ) {
191 4         55 $self->_data_sig_mask_all;
192 4         28 $self->_data_sig_pipe_finalize;
193 4         43 $self->_data_sig_pipe_build;
194 4         21 $self->_data_sig_unmask_all;
195             }
196             }
197              
198             sub _data_sig_reset_procs {
199 273     273   357 my $self = shift;
200             # Initialize this to a true value so our waitpid() loop can run at
201             # least once. Starts false when running in an Apache handler so our
202             # SIGCHLD hijinks don't interfere with the web server.
203 273         866 $self->_data_sig_cease_polling();
204 273         319 $kr_has_child_procs = BASE_SIGCHLD_COUNT;
205             }
206              
207              
208             ### Return signals that are safe to manipulate.
209              
210             sub _data_sig_get_safe_signals {
211 238     238   7487 return keys %_safe_signals;
212             }
213              
214             ### End-run leak checking.
215             our $finalizing;
216              
217             sub _data_sig_finalize {
218 190     190   360 my( $self ) = @_;
219 190         364 my $finalized_ok = 1;
220             # tell _data_sig_pipe_send to ignore CHLD that waitpid might provoke
221 190         368 local $finalizing = 1;
222              
223 190         709 $self->_data_sig_pipe_finalize;
224              
225 190         1085 while (my ($sig, $sig_rec) = each(%kr_signals)) {
226 0         0 $finalized_ok = 0;
227 0         0 _warn "!!! Leaked signal $sig\n";
228 0         0 while (my ($sid, $ses_rec) = each(%{$kr_signals{$sig}})) {
  0         0  
229 0         0 my ($event, $args, $session) = @$ses_rec;
230 0         0 _warn "!!!\t$sid = $session -> $event (@$args)\n";
231             }
232             }
233              
234 190         884 while (my ($sid, $ses_rec) = each(%kr_sessions_to_signals)) {
235 0         0 $finalized_ok = 0;
236 0         0 _warn "!!! Leaked signal cross-reference: $sid\n";
237 0         0 while (my ($sig, $sig_rec) = each(%{$kr_signals{$sid}})) {
  0         0  
238 0         0 my ($event, $args) = @$sig_rec;
239 0         0 _warn "!!!\t$sig = $event (@$args)\n";
240             }
241             }
242              
243 190         2541 while (my ($sid, $pid_rec) = each(%kr_sessions_to_pids)) {
244 0         0 $finalized_ok = 0;
245 0         0 my @pids = keys %$pid_rec;
246 0         0 _warn "!!! Leaked session to PID map: $sid -> (@pids)\n";
247             }
248              
249 190         766 while (my ($pid, $ses_rec) = each(%kr_pids_to_events)) {
250 0         0 $finalized_ok = 0;
251 0         0 _warn "!!! Leaked PID to event map: $pid\n";
252 0         0 while (my ($sid, $ev_rec, $ses) = each %$ses_rec) {
253 0         0 _warn "!!!\t$ses -> $ev_rec->[PID_EVENT] (@{$ev_rec->[PID_ARGS]})\n";
  0         0  
254             }
255             }
256              
257 190 50       609 if ($kr_has_child_procs) {
258 0         0 _warn "!!! Kernel has $kr_has_child_procs child process(es).\n";
259             }
260              
261 190 50       513 if ($polling_for_signals) {
262 0         0 _warn "!!! Finalizing signals while polling is active.\n";
263             }
264              
265 190 50       600 if (USE_SIGNAL_PIPE and $self->_data_sig_pipe_has_signals()) {
266 2         8 _warn "!!! Finalizing signals while signal pipe contains messages.\n";
267             }
268              
269 188 50       706 if (exists $kr_signals{CHLD}) {
270 2         17 _warn "!!! Finalizing signals while a blanket _child signal is watched.\n";
271             }
272              
273 190         1073 %_safe_signals = ();
274              
275 190 50       1742 unless (RUNNING_IN_HELL) {
276 190         1719 local $!;
277 190         768 local $?;
278              
279 190         1363 my $leaked_children = 0;
280              
281 188         1472 PROCESS: until ((my $pid = waitpid( -1, WNOHANG )) == -1) {
282 0         0 $finalized_ok = 0;
283 0         0 $leaked_children++;
284              
285 0 50       0 if ($pid == 0) {
286 0         0 _warn(
287             "!!! At least one child process is still running " .
288             "when POE::Kernel->run() is ready to return.\n"
289             );
290 0         0 last PROCESS;
291             }
292              
293             _warn(
294 2         8 "!!! Stopped child process (PID $pid) reaped " .
295             "when POE::Kernel->run() is ready to return.\n"
296             );
297             }
298              
299 188 100       847 if ($leaked_children) {
300 0         0 _warn("!!! Be sure to use sig_child() to reap child processes.\n");
301 0         0 _warn("!!! In extreme cases, failure to reap child processes has\n");
302 2         4 _warn("!!! resulted in a slow 'fork bomb' that has halted systems.\n");
303             }
304             }
305              
306 188         500 return $finalized_ok;
307             }
308              
309             ### Add a signal to a session.
310              
311             sub _data_sig_add {
312 365     365   1995 my ($self, $session, $signal, $event, $args) = @_;
313              
314 365         1784 my $sid = $session->ID;
315 365   100     3607 $kr_sessions_to_signals{$sid}->{$signal} = [ $event, $args || [], $session ];
316 365         2051 $self->_data_sig_signal_watch($sid, $signal);
317 365   100     3333 $kr_signals{$signal}->{$sid} = [ $event, $args || [], $session ];
318             }
319              
320             sub _data_sig_signal_watch {
321 570     570   1699 my ($self, $sid, $signal) = @_;
322              
323             # TODO - $sid not used?
324              
325             # First session to watch the signal.
326             # Ask the event loop to watch the signal.
327 570 100 100     9495 if (
      100        
      66        
328             !exists($kr_signals{$signal}) and
329             exists($_safe_signals{$signal}) and
330             ($signal ne "CHLD" or !scalar(keys %kr_sessions_to_pids))
331             ) {
332 219         3750 $self->loop_watch_signal($signal);
333             }
334             }
335              
336             sub _data_sig_signal_ignore {
337 623     623   1074 my ($self, $sid, $signal) = @_;
338              
339             # TODO - $sid not used?
340              
341 623 100 100     6412 if (
      100        
      66        
342             !exists($kr_signals{$signal}) and
343             exists($_safe_signals{$signal}) and
344             ($signal ne "CHLD" or !scalar(keys %kr_sessions_to_pids))
345             ) {
346 190         1111 $self->loop_ignore_signal($signal);
347             }
348             }
349              
350             ### Remove a signal from a session.
351              
352             sub _data_sig_remove {
353 527     527   989 my ($self, $sid, $signal) = @_;
354              
355 527         1581 delete $kr_sessions_to_signals{$sid}->{$signal};
356 527         2301 delete $kr_sessions_to_signals{$sid}
357 527 100       608 unless keys(%{$kr_sessions_to_signals{$sid}});
358              
359 527         1396 delete $kr_signals{$signal}->{$sid};
360              
361             # Last watcher for that signal. Stop watching it internally.
362 527 100       953 unless (keys %{$kr_signals{$signal}}) {
  527         1573  
363 512         757 delete $kr_signals{$signal};
364 512         1402 $self->_data_sig_signal_ignore($sid, $signal);
365             }
366             }
367              
368             ### Clear all the signals from a session.
369              
370             # XXX - It's ok to clear signals from a session that doesn't exist.
371             # Usually it means that the signals are being cleared, but it might
372             # mean that the session really doesn't exist. Should we care?
373              
374             sub _data_sig_clear_session {
375 776     776   1057 my ($self, $sid) = @_;
376              
377 776 100       1965 if (exists $kr_sessions_to_signals{$sid}) { # avoid autoviv
378 219         348 foreach (keys %{$kr_sessions_to_signals{$sid}}) {
  219         1028  
379 227         852 $self->_data_sig_remove($sid, $_);
380             }
381             }
382              
383 776 100       3601 if (exists $kr_sessions_to_pids{$sid}) { # avoid autoviv
384 10         13 foreach (keys %{$kr_sessions_to_pids{$sid}}) {
  10         28  
385 10         25 $self->_data_sig_pid_ignore($sid, $_);
386             }
387             }
388             }
389              
390             ### Watch and ignore PIDs.
391              
392             sub _data_sig_pid_watch {
393 205     205   520 my ($self, $session, $pid, $event, $args) = @_;
394              
395 205         1243 my $sid = $session->ID;
396              
397 205         2220 $kr_pids_to_events{$pid}{$sid} = [
398             $session, # PID_SESSION
399             $event, # PID_EVENT
400             $args, # PID_ARGS
401             ];
402              
403 205         1549 $self->_data_sig_signal_watch($sid, "CHLD");
404              
405 205         727 $kr_sessions_to_pids{$sid}{$pid} = 1;
406 205         970 $self->_data_ses_refcount_inc($sid);
407              
408             # Assume there's a child process. This will be corrected on the
409             # next polling interval.
410 205         17767 $kr_has_child_procs++ unless USE_SIGCHLD;
411             }
412              
413             sub _data_sig_pid_ignore {
414 153     153   622 my ($self, $sid, $pid) = @_;
415              
416             # Remove PID to event mapping.
417              
418 153         460 delete $kr_pids_to_events{$pid}{$sid};
419 153         810 delete $kr_pids_to_events{$pid} unless (
420 153 100       192 keys %{$kr_pids_to_events{$pid}}
421             );
422              
423             # Remove session to PID mapping.
424              
425 153         576 delete $kr_sessions_to_pids{$sid}{$pid};
426 153 100       253 unless (keys %{$kr_sessions_to_pids{$sid}}) {
  153         524  
427 111         254 delete $kr_sessions_to_pids{$sid};
428 111         471 $self->_data_sig_signal_ignore($sid, "CHLD");
429             }
430              
431 153         496 $self->_data_ses_refcount_dec($sid);
432             }
433              
434             sub _data_sig_session_awaits_pids {
435 8314     8314   10884 my ($self, $sid) = @_;
436              
437             # There must be child processes or pending signals.
438             # Watching PIDs doesn't matter if there are none to be reaped.
439 8314 100 100     25132 return 0 unless $kr_has_child_procs or $self->_data_sig_pipe_has_signals();
440              
441             # This session is watching at least one PID with sig_child().
442             # TODO - Watching a non-existent PID is legal but ill-advised.
443 576 100       2140 return 1 if exists $kr_sessions_to_pids{$sid};
444              
445             # Is the session waiting for a blanket sig(CHLD)?
446             return(
447 294   100     2135 (exists $kr_sessions_to_signals{$sid}) &&
448             (exists $kr_sessions_to_signals{$sid}{CHLD})
449             );
450             }
451              
452             sub _data_sig_pids_is_ses_watching {
453 1     1   2 my ($self, $sid, $pid) = @_;
454             return(
455 1   33     10 exists($kr_sessions_to_pids{$sid}) &&
456             exists($kr_sessions_to_pids{$sid}{$pid})
457             );
458             }
459              
460             ### Return a signal's type, or SIGTYPE_BENIGN if it's not special.
461              
462             sub _data_sig_type {
463 476     476   1302 my ($self, $signal) = @_;
464 476   100     2946 return $_signal_types{$signal} || SIGTYPE_BENIGN;
465             }
466              
467             ### Flag a signal as being handled by some session.
468              
469             sub _data_sig_handled {
470 127     127   1020 my $self = shift;
471 127         339 $kr_signal_total_handled++;
472             }
473              
474             ### Clear the structures associated with a signal's "handled" status.
475              
476             sub _data_sig_reset_handled {
477 474     474   1858 my ($self, $signal) = @_;
478 474         735 undef $kr_signal_total_handled;
479 474         6974 $kr_signal_type = $self->_data_sig_type($signal);
480 474         1186 undef @kr_signaled_sessions;
481             }
482              
483             ### Is the signal explicitly watched?
484              
485             sub _data_sig_explicitly_watched {
486 470     470   883 my ($self, $signal) = @_;
487 470         1852 return exists $kr_signals{$signal};
488             }
489              
490             ### Return the signals watched by a session and the events they
491             ### generate. TODO Used mainly for testing, but may also be useful
492             ### for introspection.
493              
494             sub _data_sig_watched_by_session {
495 1     1   3 my ($self, $sid) = @_;
496 1 50       3 return unless exists $kr_sessions_to_signals{$sid};
497 1         1 return %{$kr_sessions_to_signals{$sid}};
  1         5  
498             }
499              
500             ### Which sessions are watching a signal?
501              
502             sub _data_sig_watchers {
503 355     355   4307 my ($self, $signal) = @_;
504 355         437 return %{$kr_signals{$signal}};
  355         1981  
505             }
506              
507             ### Return the current signal's handled status.
508             ### TODO Used for testing.
509              
510             sub _data_sig_handled_status {
511             return(
512 475     475   2487 $kr_signal_total_handled,
513             $kr_signal_type,
514             \@kr_signaled_sessions,
515             );
516             }
517              
518             ### Determine if a given session is watching a signal. This uses a
519             ### two-step exists so that the longer one does not autovivify keys in
520             ### the shorter one.
521              
522             sub _data_sig_is_watched_by_session {
523 4     4   9 my ($self, $signal, $sid) = @_;
524             return(
525 4   100     41 exists($kr_signals{$signal}) &&
526             exists($kr_signals{$signal}->{$sid})
527             );
528             }
529              
530             ### Destroy sessions touched by a nonmaskable signal or by an
531             ### unhandled terminal signal. Check for garbage-collection on
532             ### sessions which aren't to be terminated.
533              
534             sub _data_sig_free_terminated_sessions {
535 471     471   3211 my $self = shift;
536              
537 471 100 100     4692 if (
      66        
538             ($kr_signal_type & SIGTYPE_NONMASKABLE) or
539             ($kr_signal_type & SIGTYPE_TERMINAL and !$kr_signal_total_handled)
540             ) {
541 203         617 foreach my $dead_session (@kr_signaled_sessions) {
542 286 50       979 next unless $self->_data_ses_exists($dead_session->ID);
543              
544 286         403 if (TRACE_SIGNALS) {
545             _warn(
546             " stopping signaled session ",
547             $self->_data_alias_loggable($dead_session->ID)
548             );
549             }
550              
551 286         717 $self->_data_ses_stop($dead_session->ID);
552             }
553             }
554              
555             # Erase @kr_signaled_sessions, or they will leak until the next
556             # signal.
557 446         2096 @kr_signaled_sessions = ();
558             }
559              
560             ### A signal has touched a session. Record this fact for later
561             ### destruction tests.
562              
563             sub _data_sig_touched_session {
564 1601     1410   2922 my ($self, $session) = @_;
565 1410         3247 push @kr_signaled_sessions, $session;
566             }
567              
568             # only used under !USE_SIGCHLD
569             sub _data_sig_begin_polling {
570 1     1   2 my ($self, $signal) = @_;
571              
572 1 50       3 return if $polling_for_signals;
573 1         5 $polling_for_signals = 1;
574              
575 1         5 $self->_data_sig_enqueue_poll_event($signal);
576 1         4 $self->_idle_queue_grow();
577             }
578              
579             # only used under !USE_SIGCHLD
580             sub _data_sig_cease_polling {
581 280     280   450 $polling_for_signals = 0;
582             }
583              
584             sub _data_sig_enqueue_poll_event {
585 398     398   1750 my ($self, $signal) = @_;
586              
587 398         560 if ( USE_SIGCHLD ) {
588 398 100       1155 return if $polling_for_signals;
589 392         1495 $polling_for_signals = 1;
590              
591 392         4201 $self->_data_ev_enqueue(
592             $self, $self, EN_SCPOLL, ET_SCPOLL, [ $signal ],
593             __FILE__, __LINE__, undef
594             );
595             } else {
596             return if $self->_data_ses_count() < 1;
597             return unless $polling_for_signals;
598              
599             $self->_data_ev_enqueue(
600             $self, $self, EN_SCPOLL, ET_SCPOLL, [ $signal ],
601             __FILE__, __LINE__, undef, walltime(), POE::Kernel::CHILD_POLLING_INTERVAL(),
602             );
603             }
604             }
605              
606             sub _data_sig_handle_poll_event {
607 360     360   1036 my ($self, $signal) = @_;
608              
609 360         436 if ( USE_SIGCHLD ) {
610 360         927 $polling_for_signals = undef;
611             }
612              
613 360         399 if (TRACE_SIGNALS) {
614             _warn(
615             " POE::Kernel is polling for signals at " . monotime() .
616             (USE_SIGCHLD ? " due to SIGCHLD" : "")
617             );
618             }
619              
620 360         1860 $self->_data_sig_reap_pids();
621              
622             # The poll loop is over. Resume slowly polling for signals.
623              
624 360         1250 if (USE_SIGCHLD) {
625 360         356 if (TRACE_SIGNALS) {
626             _warn(" POE::Kernel has reset the SIG$signal handler");
627             }
628             # Per https://rt.cpan.org/Ticket/Display.html?id=45109 setting the
629             # signal handler must be done after reaping the outstanding child
630             # processes, at least on SysV systems like HP-UX.
631 360         4126 $SIG{$signal} = \&_loop_signal_handler_chld;
632             }
633             else {
634             # The poll loop is over. Resume slowly polling for signals.
635              
636             if ($polling_for_signals) {
637             if (TRACE_SIGNALS) {
638             _warn(" POE::Kernel will poll again after a delay");
639             }
640             $self->_data_sig_enqueue_poll_event($signal);
641             }
642             else {
643             if (TRACE_SIGNALS) {
644             _warn(" POE::Kernel SIGCHLD poll loop paused");
645             }
646             $self->_idle_queue_shrink();
647             }
648             }
649             }
650              
651             sub _data_sig_reap_pids {
652 396     360   757 my $self = shift();
653              
654             # Reap children for as long as waitpid(2) says something
655             # interesting has happened.
656             # TODO This has a possibility of an infinite loop, but so far it
657             # hasn't hasn't happened.
658              
659 396         1491 my $pid;
660 360         12197 while ($pid = waitpid(-1, WNOHANG)) {
661             # waitpid(2) returned a process ID. Emit an appropriate SIGCHLD
662             # event and loop around again.
663              
664 398 100 33     2310 if (($pid > 0) or (RUNNING_IN_HELL and $pid < -1)) {
      66        
665 226 50 66     1031 if (RUNNING_IN_HELL or WIFEXITED($?) or WIFSIGNALED($?)) {
      66        
666              
667 226         297 if (TRACE_SIGNALS) {
668             _warn(" POE::Kernel detected SIGCHLD (pid=$pid; exit=$?)");
669             }
670              
671             # Check for explicit SIGCHLD watchers, and enqueue explicit
672             # events for them.
673              
674 226 100       988 if (exists $kr_pids_to_events{$pid}) {
675 145         692 my @sessions_to_clear;
676 127         174 while (my ($sid, $ses_rec) = each %{$kr_pids_to_events{$pid}}) {
  216         1055  
677 143         735 $self->_data_ev_enqueue(
678             $ses_rec->[PID_SESSION], $self, $ses_rec->[PID_EVENT], ET_SIGCLD,
679 181         726 [ 'CHLD', $pid, $?, @{$ses_rec->[PID_ARGS]} ],
680             __FILE__, __LINE__, undef
681             );
682 143         628 push @sessions_to_clear, $sid;
683             }
684 143         637 $self->_data_sig_pid_ignore($_, $pid) foreach @sessions_to_clear;
685             }
686              
687             # Kick off a SIGCHLD cascade.
688             $self->_data_ev_enqueue(
689 208         1445 $self, $self, EN_SIGNAL, ET_SIGNAL, [ 'CHLD', $pid, $? ],
690             __FILE__, __LINE__, undef
691             );
692             }
693 56         411 elsif (TRACE_SIGNALS) {
694             _warn(" POE::Kernel detected strange exit (pid=$pid; exit=$?");
695             }
696              
697 170         308 if (TRACE_SIGNALS) {
698             _warn(" POE::Kernel will poll again immediately");
699             }
700              
701 226         723 next;
702             }
703              
704             # The only other negative value waitpid(2) should return is -1.
705             # This is highly unlikely, but it's necessary to catch
706             # portability problems.
707             #
708             # TODO - Find a way to test this.
709              
710 213 50       607 _trap "internal consistency error: waitpid returned $pid" if $pid != -1;
711              
712             # If the error is an interrupted syscall, poll again right away.
713              
714 213 50       2221 if ($! == EINTR) {
715 15         54 if (TRACE_SIGNALS) {
716             _warn(
717             " POE::Kernel's waitpid(2) was interrupted.\n",
718             "POE::Kernel will poll again immediately.\n"
719             );
720             }
721 15         141 next;
722             }
723              
724             # No child processes exist. TODO This is different than
725             # children being present but running. Maybe this condition
726             # could halt polling entirely, and some UNIVERSAL::fork wrapper
727             # could restart polling when processes are forked.
728              
729 157 50       600 if ($! == ECHILD) {
730 157         210 if (TRACE_SIGNALS) {
731             _warn(" POE::Kernel has no child processes");
732             }
733 157         277 last;
734             }
735              
736             # Some other error occurred.
737              
738 15         62 if (TRACE_SIGNALS) {
739             _warn(" POE::Kernel's waitpid(2) got error: $!");
740             }
741 15         23 last;
742             }
743              
744             # Remember whether there are more processes to reap.
745              
746 339         1031 $kr_has_child_procs = !$pid;
747             }
748              
749             # Are there child processes worth waiting for?
750             # We don't really care if we're not polling for signals.
751              
752             sub _data_sig_kernel_awaits_pids {
753 2417     2402   3508 my $self = shift();
754              
755 2402         2212 return 0 if !USE_SIGCHLD and !$polling_for_signals;
756              
757             # There must be child processes or pending signals.
758 2401 100 100     8175 return 0 unless $kr_has_child_procs or $self->_data_sig_pipe_has_signals();
759              
760             # At least one session is watching an explicit PID.
761             # TODO - Watching a non-existent PID is legal but ill-advised.
762 178 100       854 return 1 if scalar keys %kr_pids_to_events;
763              
764             # Is the session waiting for a blanket sig(CHLD)?
765 137         673 return exists $kr_signals{CHLD};
766             }
767              
768             ######################
769             ## Safe signals, the final solution:
770             ## Semantically, signal handlers and the main loop are in different threads.
771             ## To avoid all possible deadlock and race conditions once and for all we
772             ## implement them as shared-nothing threads.
773             ##
774             ## The signal handlers are split in 2 :
775             ## - a top handler, which sends the signal number over a one-way pipe.
776             ## - a bottom handler, which is called when this number is received in the
777             ## main loop.
778             ## The top handler will send a packet of PID and number. We need the PID
779             ## because of the race condition with signals in perl; signals meant for the
780             ## parent end up in both the parent and child. So we check the PID to make
781             ## sure it was intended for the child. We use 'ii' (2 ints, aka 8 bytes)
782             ## and not 'iC' (int+byte, aka 5 bytes) because we want a small factor of
783             ## the buffer size in the hopes of never getting a short read. Ever.
784              
785 175     175   4562 use vars qw( $signal_pipe_read_fd );
  175         250  
  175         179394  
786             my(
787             $signal_pipe_write,
788             $signal_pipe_read,
789             $signal_pipe_pid,
790             $signal_mask_none,
791             $signal_mask_all,
792              
793             @pending_signals,
794             );
795              
796             sub SIGINFO_NAME () { 0 }
797             sub SIGINFO_SRC_PID () { 1 }
798              
799              
800             sub _data_sig_pipe_has_signals {
801 10168     10168   11585 my $self = shift();
802 10168 100       20825 return unless $signal_pipe_read;
803 9446         10130 my $vec = '';
804 9446         30696 vec($vec, fileno($signal_pipe_read), 1) = 1;
805              
806             # Ambiguous call resolved as CORE::select(), qualify as such or use &
807 9446         83941 return(CORE::select($vec, undef, undef, 0) > 0);
808             }
809              
810              
811             sub _data_sig_pipe_build {
812 269     269   393 my( $self ) = @_;
813 269         316 return unless USE_SIGNAL_PIPE;
814 269         381 my $fake = 128;
815              
816             # Associate the pipe with this PID
817 269         572 $signal_pipe_pid = $$;
818              
819             # Mess with the signal mask
820 269         780 $self->_data_sig_mask_all;
821              
822             # Open the signal pipe.
823             # TODO - Normally POE::Pipe::OneWay will do the right thing. Why
824             # are we overriding its per-platform autodetection?
825 269 50       682 if (RUNNING_IN_HELL) {
826 0         0 ( $signal_pipe_read, $signal_pipe_write ) = POE::Pipe::OneWay->new('inet');
827             }
828             else {
829 269         2166 ( $signal_pipe_read, $signal_pipe_write ) = POE::Pipe::OneWay->new('pipe');
830             }
831              
832 269 50       22543 unless ($signal_pipe_write) {
833 0         0 _trap " Error " . ($!+0) . " trying to create the signal pipe: $!";
834             }
835              
836             # Allows Resource::FileHandles to by-pass the queue
837 269         614 $signal_pipe_read_fd = fileno $signal_pipe_read;
838 269         387 if( TRACE_SIGNALS ) {
839             _warn " signal_pipe_write=$signal_pipe_write";
840             _warn " signal_pipe_read=$signal_pipe_read";
841             _warn " signal_pipe_read_fd=$signal_pipe_read_fd";
842             }
843              
844             # Add to the select list
845 269         1242 $self->_data_handle_condition( $signal_pipe_read );
846 269         1917 $self->loop_watch_filehandle( $signal_pipe_read, MODE_RD );
847 269         1447 $self->_data_sig_unmask_all;
848             }
849              
850             sub _data_sig_mask_build {
851 298 50   173   1915 return if RUNNING_IN_HELL;
852 298         2676 $signal_mask_none = POSIX::SigSet->new();
853 298         2003 $signal_mask_none->emptyset();
854 173         550 $signal_mask_all = POSIX::SigSet->new();
855 173         600 $signal_mask_all->fillset();
856             }
857              
858             ### Mask all signals
859             sub _data_sig_mask_all {
860 562 50   562   2068 return if RUNNING_IN_HELL;
861 562         962 my $self = $poe_kernel;
862 562 100       1766 unless( $signal_mask_all ) {
863 173         391 $self->_data_sig_mask_build;
864             }
865 562         4571 my $mask_temp = POSIX::SigSet->new();
866 562 50       5615 sigprocmask( SIG_SETMASK, $signal_mask_all, $mask_temp )
867             or _trap " Unable to mask all signals: $!";
868             }
869              
870             ### Unmask all signals
871             sub _data_sig_unmask_all {
872 562 50   562   5308 return if RUNNING_IN_HELL;
873 562         3327 my $self = $poe_kernel;
874 562 50       2464 unless( $signal_mask_none ) {
875 0         0 $self->_data_sig_mask_build;
876             }
877 562         7746 my $mask_temp = POSIX::SigSet->new();
878 562 50       4816 sigprocmask( SIG_SETMASK, $signal_mask_none, $mask_temp )
879             or _trap " Unable to unmask all signals: $!";
880             }
881              
882              
883              
884             sub _data_sig_pipe_finalize {
885 194     194   313 my( $self ) = @_;
886 194 100       1051 if( $signal_pipe_read ) {
887 192         1067 $self->loop_ignore_filehandle( $signal_pipe_read, MODE_RD );
888 192         1838 close $signal_pipe_read; undef $signal_pipe_read;
  192         439  
889             }
890 194 100       2265 if( $signal_pipe_write ) {
891 192         1629 close $signal_pipe_write; undef $signal_pipe_write;
  192         328  
892             }
893             # Don't send anything more!
894 194         612 undef( $signal_pipe_pid );
895             }
896              
897             ### Send a signal "message" to the main thread
898             ### Called from the top signal handlers
899             sub _data_sig_pipe_send {
900 218     218   17807 local $!;
901              
902 218         761 my $signal_name = $_[1];
903              
904 218         322 if( TRACE_SIGNALS ) {
905             _warn " Caught SIG$signal_name";
906             }
907              
908 218 50       1146 return if $finalizing;
909              
910 218 50       1846 if( not defined $signal_pipe_pid ) {
911 55         219 _trap " _data_sig_pipe_send called before signal pipe was initialized.";
912             }
913              
914             # ugh- has_forked() can't be called fast enough. This warning might
915             # show up before it is called. Should we just detect forking and do it
916             # for the user? Probably not...
917              
918 163 50       1012 if( $$ != $signal_pipe_pid ) {
919 55         255 _warn(
920             " Kernel now running in a different process " .
921             "(is=$$ was=$signal_pipe_pid ). " .
922             "You must call call \$poe_kernel->has_forked() in the child process."
923             );
924             }
925              
926             # We're registering signals in a list. Pipes have more finite
927             # capacity, so we'll just write a single-byte semaphore-like token.
928             # It's up to the reader to process the list. Duplicates are
929             # permitted, and their ordering may be significant. Precedent:
930             # http://search.cpan.org/perldoc?IPC%3A%3AMorseSignals
931              
932 163         788 push @pending_signals, [
933             $signal_name, # SIGINFO_NAME
934             $$, # SIGINFO_SRC_PID
935             ];
936              
937 218         523 if (TRACE_SIGNALS) {
938             _warn " Attempting signal pipe write";
939             }
940              
941 218         1765 my $count = syswrite( $signal_pipe_write, '!' );
942              
943             # TODO - We need to crash gracefully if the write fails, but not if
944             # it's due to the pipe being full. We might solve this by only
945             # writing on the edge of @pending_signals == 1 after the push().
946             # We assume @pending_signals > 1 means there's a byte in the pipe,
947             # so the reader will wake up to catch 'em all.
948              
949 218         1062 if ( ASSERT_DATA ) {
950             unless (defined $count and $count == 1) {
951             _trap " Signal pipe write failed: $!";
952             }
953             }
954             }
955              
956             ### Read all signal numbers.
957             ### Call the related bottom handler. That is, inside the kernel loop.
958             sub _data_sig_pipe_read {
959 328     198   2787 my( $self, $fileno, $mode ) = @_;
960              
961 253         403 if( ASSERT_DATA ) {
962             _trap "Illegal mode=$mode on fileno=$fileno" unless
963             $fileno == $signal_pipe_read_fd
964             and $mode eq MODE_RD;
965             }
966              
967             # Read all data from the signal pipe.
968             # The data itself doesn't matter.
969             # TODO - If writes can happen on the edge of @pending_signals (from
970             # 0 to 1 element), then we oughtn't need to loop here.
971              
972 253         2076 while (1) {
973 286         1429 my $octets_count = sysread( $signal_pipe_read, (my $data), 65536 );
974              
975 396 100       1956 next if $octets_count;
976 308 100       1008 last if defined $octets_count;
977              
978 198 100 66     1327 last if $! == EAGAIN or $! == EWOULDBLOCK;
979              
980 110         1109 if (ASSERT_DATA) {
981             _trap " Error " . ($!+0) . " reading from signal pipe: $!";
982             }
983 0         0 elsif(TRACE_SIGNALS) {
984             _warn " Error " . ($!+0) . " reading from signal pipe: $!";
985             }
986              
987 0         0 last;
988             }
989              
990             # Double buffer signals.
991             # The intent is to avoid a race condition by processing the same
992             # buffer that new signals go into.
993              
994 88 100       290 return unless @pending_signals;
995 198         3703 my @signals = @pending_signals;
996 198         603 @pending_signals = ();
997              
998 198         342 if (TRACE_SIGNALS) {
999             _warn " Read " . scalar(@signals) . " signals from the list";
1000             }
1001              
1002 198         431 foreach my $signal (@signals) {
1003 198         588 my $signal_name = $signal->[SIGINFO_NAME];
1004 198         1063 my $signal_src_pid = $signal->[SIGINFO_SRC_PID];
1005              
1006             # Ignore signals from other processes.
1007             # This can happen if we've fork()ed without calling has_forked()
1008             # to reset the signals subsystem.
1009             #
1010             # TODO - We might be able to get rid of has_forked() if PID
1011             # mismatches are detected.
1012              
1013 218 50       575 next if $signal_src_pid != $$;
1014              
1015 218 100       621 if( $signal_name eq 'CHLD' ) {
    100          
1016 218         860 _loop_signal_handler_chld_bottom( $signal_name );
1017             }
1018             elsif( $signal_name eq 'PIPE' ) {
1019 130         535 _loop_signal_handler_pipe_bottom( $signal_name );
1020             }
1021             else {
1022 27         117 _loop_signal_handler_generic_bottom( $signal_name );
1023             }
1024             }
1025             }
1026              
1027             1;
1028              
1029             __END__