File Coverage

blib/lib/POE/Resource/Signals.pm
Criterion Covered Total %
statement 289 325 88.9
branch 92 126 73.0
condition 52 66 78.7
subroutine 47 47 100.0
pod n/a
total 480 564 85.1


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 176     176   863 use vars qw($VERSION);
  176         238  
  176         9483  
7             $VERSION = '1.366'; # NOTE - Should be #.### (three decimal places)
8              
9             # These methods are folded into POE::Kernel;
10             package POE::Kernel;
11              
12 176     176   788 use strict;
  176         226  
  176         4639  
13              
14 176     176   761 use POE::Pipe::OneWay;
  176         224  
  176         2981  
15 176     176   693 use POE::Resource::FileHandles;
  176         215  
  176         4022  
16 176     176   704 use POSIX qw(:sys_wait_h sigprocmask SIG_SETMASK);
  176         203  
  176         1400  
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   36 my ($self, $old_id, $new_id) = @_;
68              
69 4         122 while (my ($signal, $sig_rec) = each %kr_signals) {
70 4 50       33 next unless exists $sig_rec->{$old_id};
71 4         86 $sig_rec->{$new_id} = delete $sig_rec->{$old_id};
72             }
73              
74 4 50       74 $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         60 while (my ($pid, $pid_rec) = each %kr_pids_to_events) {
78 2 50       31 next unless exists $pid_rec->{$old_id};
79 0         0 $pid_rec->{$new_id} = delete $pid_rec->{$old_id};
80             }
81              
82 4 50       54 $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 176         10135 '@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 176     176   72404 );
  176         415  
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 176     176   782 use constant BASE_SIGCHLD_COUNT => 0;
  176         309  
  176         457747  
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 270     270   665 my $self = shift;
143              
144 270         1049 $self->_data_sig_reset_procs;
145              
146 270         532 $poe_kernel->[KR_SIGNALS] = \%kr_signals;
147 270         509 $poe_kernel->[KR_PIDS] = \%kr_pids_to_events;
148              
149             # In case we're called multiple times.
150 270 100       939 unless (keys %_safe_signals) {
151 269         5309 foreach my $signal (keys %SIG) {
152              
153             # Nonexistent signals, and ones which are globally unhandled.
154             next if (
155 18420 100       36514 $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 9146 50 66     15682 next if $signal eq 'BUS' and RUNNING_IN_HELL;
169              
170             # Apache uses SIGCHLD and/or SIGCLD itself, so we can't.
171 9146 50 66     17420 next if $signal =~ /^CH?LD$/ and exists $INC{'Apache.pm'};
172              
173 9146         12491 $_safe_signals{$signal} = 1;
174             }
175              
176             # Reset some important signal handlers. The rest remain
177             # untouched.
178              
179 269 50       2791 $self->loop_ignore_signal("CHLD") if exists $SIG{CHLD};
180 268 50       1406 $self->loop_ignore_signal("CLD") if exists $SIG{CLD};
181 268 50       1352 $self->loop_ignore_signal("PIPE") if exists $SIG{PIPE};
182              
183 268         856 $self->_data_sig_pipe_build if USE_SIGNAL_PIPE;
184             }
185             }
186              
187             sub _data_sig_has_forked {
188 4     4   20 my( $self ) = @_;
189 4         33 $self->_data_sig_reset_procs;
190 4         5 if( USE_SIGNAL_PIPE ) {
191 4         43 $self->_data_sig_mask_all;
192 4         24 $self->_data_sig_pipe_finalize;
193 4         43 $self->_data_sig_pipe_build;
194 4         11 $self->_data_sig_unmask_all;
195             }
196             }
197              
198             sub _data_sig_reset_procs {
199 274     274   405 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 274         747 $self->_data_sig_cease_polling();
204 274         421 $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 239     239   6698 return keys %_safe_signals;
212             }
213              
214             ### End-run leak checking.
215             our $finalizing;
216              
217             sub _data_sig_finalize {
218 191     191   345 my( $self ) = @_;
219 191         317 my $finalized_ok = 1;
220             # tell _data_sig_pipe_send to ignore CHLD that waitpid might provoke
221 191         363 local $finalizing = 1;
222              
223 191         815 $self->_data_sig_pipe_finalize;
224              
225 191         1017 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 191         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 191         2455 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 191         785 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 191 50       622 if ($kr_has_child_procs) {
258 0         0 _warn "!!! Kernel has $kr_has_child_procs child process(es).\n";
259             }
260              
261 191 50       540 if ($polling_for_signals) {
262 0         0 _warn "!!! Finalizing signals while polling is active.\n";
263             }
264              
265 191 50       602 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 189 50       636 if (exists $kr_signals{CHLD}) {
270 2         14 _warn "!!! Finalizing signals while a blanket _child signal is watched.\n";
271             }
272              
273 191         1060 %_safe_signals = ();
274              
275 191 50       1585 unless (RUNNING_IN_HELL) {
276 191         1711 local $!;
277 191         1658 local $?;
278              
279 191         627 my $leaked_children = 0;
280              
281 189         1530 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         7 "!!! Stopped child process (PID $pid) reaped " .
295             "when POE::Kernel->run() is ready to return.\n"
296             );
297             }
298              
299 189 100       784 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         5 _warn("!!! resulted in a slow 'fork bomb' that has halted systems.\n");
303             }
304             }
305              
306 189         460 return $finalized_ok;
307             }
308              
309             ### Add a signal to a session.
310              
311             sub _data_sig_add {
312 366     366   1904 my ($self, $session, $signal, $event, $args) = @_;
313              
314 366         1731 my $sid = $session->ID;
315 366   100     3585 $kr_sessions_to_signals{$sid}->{$signal} = [ $event, $args || [], $session ];
316 366         1160 $self->_data_sig_signal_watch($sid, $signal);
317 366   100     3431 $kr_signals{$signal}->{$sid} = [ $event, $args || [], $session ];
318             }
319              
320             sub _data_sig_signal_watch {
321 585     585   1780 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 585 100 100     6852 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 220         3292 $self->loop_watch_signal($signal);
333             }
334             }
335              
336             sub _data_sig_signal_ignore {
337 648     648   1108 my ($self, $sid, $signal) = @_;
338              
339             # TODO - $sid not used?
340              
341 648 100 100     6660 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 191         1021 $self->loop_ignore_signal($signal);
347             }
348             }
349              
350             ### Remove a signal from a session.
351              
352             sub _data_sig_remove {
353 540     540   1229 my ($self, $sid, $signal) = @_;
354              
355 540         1645 delete $kr_sessions_to_signals{$sid}->{$signal};
356 540         2228 delete $kr_sessions_to_signals{$sid}
357 540 100       736 unless keys(%{$kr_sessions_to_signals{$sid}});
358              
359 540         1370 delete $kr_signals{$signal}->{$sid};
360              
361             # Last watcher for that signal. Stop watching it internally.
362 540 100       858 unless (keys %{$kr_signals{$signal}}) {
  540         1541  
363 525         721 delete $kr_signals{$signal};
364 525         1528 $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 791     791   1144 my ($self, $sid) = @_;
376              
377 791 100       2150 if (exists $kr_sessions_to_signals{$sid}) { # avoid autoviv
378 220         339 foreach (keys %{$kr_sessions_to_signals{$sid}}) {
  220         934  
379 228         724 $self->_data_sig_remove($sid, $_);
380             }
381             }
382              
383 791 100       4031 if (exists $kr_sessions_to_pids{$sid}) { # avoid autoviv
384 10         12 foreach (keys %{$kr_sessions_to_pids{$sid}}) {
  10         36  
385 10         29 $self->_data_sig_pid_ignore($sid, $_);
386             }
387             }
388             }
389              
390             ### Watch and ignore PIDs.
391              
392             sub _data_sig_pid_watch {
393 219     219   481 my ($self, $session, $pid, $event, $args) = @_;
394              
395 219         1034 my $sid = $session->ID;
396              
397 219         2036 $kr_pids_to_events{$pid}{$sid} = [
398             $session, # PID_SESSION
399             $event, # PID_EVENT
400             $args, # PID_ARGS
401             ];
402              
403 219         1354 $self->_data_sig_signal_watch($sid, "CHLD");
404              
405 219         728 $kr_sessions_to_pids{$sid}{$pid} = 1;
406 219         864 $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 219         15457 $kr_has_child_procs++ unless USE_SIGCHLD;
411             }
412              
413             sub _data_sig_pid_ignore {
414 167     167   315 my ($self, $sid, $pid) = @_;
415              
416             # Remove PID to event mapping.
417              
418 167         570 delete $kr_pids_to_events{$pid}{$sid};
419 167         803 delete $kr_pids_to_events{$pid} unless (
420 167 100       403 keys %{$kr_pids_to_events{$pid}}
421             );
422              
423             # Remove session to PID mapping.
424              
425 167         650 delete $kr_sessions_to_pids{$sid}{$pid};
426 167 100       220 unless (keys %{$kr_sessions_to_pids{$sid}}) {
  167         581  
427 123         367 delete $kr_sessions_to_pids{$sid};
428 123         495 $self->_data_sig_signal_ignore($sid, "CHLD");
429             }
430              
431 167         588 $self->_data_ses_refcount_dec($sid);
432             }
433              
434             sub _data_sig_session_awaits_pids {
435 8781     8781   10756 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 8781 100 66     26080 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 868 100       2904 return 1 if exists $kr_sessions_to_pids{$sid};
444              
445             # Is the session waiting for a blanket sig(CHLD)?
446             return(
447 472   100     3273 (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     7 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 491     491   1411 my ($self, $signal) = @_;
464 491   100     2733 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   1052 my $self = shift;
471 127         289 $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 489     489   1971 my ($self, $signal) = @_;
478 489         6782 undef $kr_signal_total_handled;
479 489         1418 $kr_signal_type = $self->_data_sig_type($signal);
480 489         1190 undef @kr_signaled_sessions;
481             }
482              
483             ### Is the signal explicitly watched?
484              
485             sub _data_sig_explicitly_watched {
486 485     485   805 my ($self, $signal) = @_;
487 485         1730 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   2 my ($self, $sid) = @_;
496 1 50       4 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 356     356   3152 my ($self, $signal) = @_;
504 356         442 return %{$kr_signals{$signal}};
  356         1850  
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 490     490   2366 $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     26 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 486     486   3417 my $self = shift;
536              
537 486 100 100     3518 if (
      66        
538             ($kr_signal_type & SIGTYPE_NONMASKABLE) or
539             ($kr_signal_type & SIGTYPE_TERMINAL and !$kr_signal_total_handled)
540             ) {
541 204         638 foreach my $dead_session (@kr_signaled_sessions) {
542 287 50       2028 next unless $self->_data_ses_exists($dead_session->ID);
543              
544 287         440 if (TRACE_SIGNALS) {
545             _warn(
546             " stopping signaled session ",
547             $self->_data_alias_loggable($dead_session->ID)
548             );
549             }
550              
551 287         682 $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 447         1901 @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 1755     1549   3147 my ($self, $session) = @_;
565 1549         3393 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         4 $polling_for_signals = 1;
574              
575 1         7 $self->_data_sig_enqueue_poll_event($signal);
576 1         5 $self->_idle_queue_grow();
577             }
578              
579             # only used under !USE_SIGCHLD
580             sub _data_sig_cease_polling {
581 281     281   390 $polling_for_signals = 0;
582             }
583              
584             sub _data_sig_enqueue_poll_event {
585 409     409   1968 my ($self, $signal) = @_;
586              
587 409         576 if ( USE_SIGCHLD ) {
588 409 100       1173 return if $polling_for_signals;
589 401         893 $polling_for_signals = 1;
590              
591 401         4502 $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 369     369   767 my ($self, $signal) = @_;
608              
609 369         494 if ( USE_SIGCHLD ) {
610 369         954 $polling_for_signals = undef;
611             }
612              
613 369         406 if (TRACE_SIGNALS) {
614             _warn(
615             " POE::Kernel is polling for signals at " . monotime() .
616             (USE_SIGCHLD ? " due to SIGCHLD" : "")
617             );
618             }
619              
620 369         1734 $self->_data_sig_reap_pids();
621              
622             # The poll loop is over. Resume slowly polling for signals.
623              
624 369         1117 if (USE_SIGCHLD) {
625 369         440 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 369         3684 $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 415     369   731 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 415         1529 my $pid;
660 369         9611 while ($pid = waitpid(-1, WNOHANG)) {
661             # waitpid(2) returned a process ID. Emit an appropriate SIGCHLD
662             # event and loop around again.
663              
664 413 100 33     2233 if (($pid > 0) or (RUNNING_IN_HELL and $pid < -1)) {
      66        
665 240 50 66     972 if (RUNNING_IN_HELL or WIFEXITED($?) or WIFSIGNALED($?)) {
      66        
666              
667 240         319 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 240 100       1257 if (exists $kr_pids_to_events{$pid}) {
675 159         729 my @sessions_to_clear;
676 141         199 while (my ($sid, $ses_rec) = each %{$kr_pids_to_events{$pid}}) {
  230         1101  
677 157         684 $self->_data_ev_enqueue(
678             $ses_rec->[PID_SESSION], $self, $ses_rec->[PID_EVENT], ET_SIGCLD,
679 209         793 [ 'CHLD', $pid, $?, @{$ses_rec->[PID_ARGS]} ],
680             __FILE__, __LINE__, undef
681             );
682 157         721 push @sessions_to_clear, $sid;
683             }
684 157         617 $self->_data_sig_pid_ignore($_, $pid) foreach @sessions_to_clear;
685             }
686              
687             # Kick off a SIGCHLD cascade.
688             $self->_data_ev_enqueue(
689 222         1492 $self, $self, EN_SIGNAL, ET_SIGNAL, [ 'CHLD', $pid, $? ],
690             __FILE__, __LINE__, undef
691             );
692             }
693 70         529 elsif (TRACE_SIGNALS) {
694             _warn(" POE::Kernel detected strange exit (pid=$pid; exit=$?");
695             }
696              
697 170         285 if (TRACE_SIGNALS) {
698             _warn(" POE::Kernel will poll again immediately");
699             }
700              
701 240         938 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 227 50       632 _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 227 50       2253 if ($! == EINTR) {
715 16         75 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 16         156 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       520 if ($! == ECHILD) {
730 157         145 if (TRACE_SIGNALS) {
731             _warn(" POE::Kernel has no child processes");
732             }
733 157         224 last;
734             }
735              
736             # Some other error occurred.
737              
738 16         81 if (TRACE_SIGNALS) {
739             _warn(" POE::Kernel's waitpid(2) got error: $!");
740             }
741 16         30 last;
742             }
743              
744             # Remember whether there are more processes to reap.
745              
746 339         910 $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 2478     2462   3685 my $self = shift();
754              
755 2462         2403 return 0 if !USE_SIGCHLD and !$polling_for_signals;
756              
757             # There must be child processes or pending signals.
758 2461 100 100     8454 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 238 100       1132 return 1 if scalar keys %kr_pids_to_events;
763              
764             # Is the session waiting for a blanket sig(CHLD)?
765 147         683 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 176     176   4545 use vars qw( $signal_pipe_read_fd );
  176         323  
  176         170841  
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 10339     10339   12007 my $self = shift();
802 10339 100       20630 return unless $signal_pipe_read;
803 9614         10084 my $vec = '';
804 9614         29654 vec($vec, fileno($signal_pipe_read), 1) = 1;
805              
806             # Ambiguous call resolved as CORE::select(), qualify as such or use &
807 9614         82658 return(CORE::select($vec, undef, undef, 0) > 0);
808             }
809              
810              
811             sub _data_sig_pipe_build {
812 270     270   432 my( $self ) = @_;
813 270         280 return unless USE_SIGNAL_PIPE;
814 270         418 my $fake = 128;
815              
816             # Associate the pipe with this PID
817 270         535 $signal_pipe_pid = $$;
818              
819             # Mess with the signal mask
820 270         724 $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 270 50       652 if (RUNNING_IN_HELL) {
826 0         0 ( $signal_pipe_read, $signal_pipe_write ) = POE::Pipe::OneWay->new('inet');
827             }
828             else {
829 270         2134 ( $signal_pipe_read, $signal_pipe_write ) = POE::Pipe::OneWay->new('pipe');
830             }
831              
832 270 50       21486 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 270         676 $signal_pipe_read_fd = fileno $signal_pipe_read;
838 270         354 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 270         1308 $self->_data_handle_condition( $signal_pipe_read );
846 270         1591 $self->loop_watch_filehandle( $signal_pipe_read, MODE_RD );
847 270         1204 $self->_data_sig_unmask_all;
848             }
849              
850             sub _data_sig_mask_build {
851 300 50   174   1421 return if RUNNING_IN_HELL;
852 300         2727 $signal_mask_none = POSIX::SigSet->new();
853 300         1967 $signal_mask_none->emptyset();
854 174         578 $signal_mask_all = POSIX::SigSet->new();
855 174         544 $signal_mask_all->fillset();
856             }
857              
858             ### Mask all signals
859             sub _data_sig_mask_all {
860 577 50   577   2101 return if RUNNING_IN_HELL;
861 577         888 my $self = $poe_kernel;
862 577 100       1681 unless( $signal_mask_all ) {
863 174         407 $self->_data_sig_mask_build;
864             }
865 577         4490 my $mask_temp = POSIX::SigSet->new();
866 577 50       5476 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 577 50   577   5308 return if RUNNING_IN_HELL;
873 577         2600 my $self = $poe_kernel;
874 577 50       2147 unless( $signal_mask_none ) {
875 0         0 $self->_data_sig_mask_build;
876             }
877 577         7090 my $mask_temp = POSIX::SigSet->new();
878 577 50       4694 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 195     195   989 my( $self ) = @_;
886 195 100       979 if( $signal_pipe_read ) {
887 193         952 $self->loop_ignore_filehandle( $signal_pipe_read, MODE_RD );
888 193         3257 close $signal_pipe_read; undef $signal_pipe_read;
  193         451  
889             }
890 195 100       1480 if( $signal_pipe_write ) {
891 193         1822 close $signal_pipe_write; undef $signal_pipe_write;
  193         5330  
892             }
893             # Don't send anything more!
894 195         591 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 228     228   3398 local $!;
901              
902 228         714 my $signal_name = $_[1];
903              
904 228         435 if( TRACE_SIGNALS ) {
905             _warn " Caught SIG$signal_name";
906             }
907              
908 228 50       1177 return if $finalizing;
909              
910 228 50       1703 if( not defined $signal_pipe_pid ) {
911 65         243 _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       1201 if( $$ != $signal_pipe_pid ) {
919 65         229 _warn(
920             " Signal caught in different process than POE::Kernel initialized " .
921             "(newPID=$$ oldPID=$signal_pipe_pid sig=$signal_name).\n"
922             );
923 0         0 _warn(
924             "Call POE::Kernel->has_forked() in the child process " .
925             "to relocate the signal handler.\n"
926             );
927             }
928              
929             # We're registering signals in a list. Pipes have more finite
930             # capacity, so we'll just write a single-byte semaphore-like token.
931             # It's up to the reader to process the list. Duplicates are
932             # permitted, and their ordering may be significant. Precedent:
933             # http://search.cpan.org/perldoc?IPC%3A%3AMorseSignals
934              
935 163         799 push @pending_signals, [
936             $signal_name, # SIGINFO_NAME
937             $$, # SIGINFO_SRC_PID
938             ];
939              
940 228         500 if (TRACE_SIGNALS) {
941             _warn " Attempting signal pipe write";
942             }
943              
944 228         2309 my $count = syswrite( $signal_pipe_write, '!' );
945              
946             # TODO - We need to crash gracefully if the write fails, but not if
947             # it's due to the pipe being full. We might solve this by only
948             # writing on the edge of @pending_signals == 1 after the push().
949             # We assume @pending_signals > 1 means there's a byte in the pipe,
950             # so the reader will wake up to catch 'em all.
951              
952 228         1451 if ( ASSERT_DATA ) {
953             unless (defined $count and $count == 1) {
954             _trap " Signal pipe write failed: $!";
955             }
956             }
957             }
958              
959             ### Read all signal numbers.
960             ### Call the related bottom handler. That is, inside the kernel loop.
961             sub _data_sig_pipe_read {
962 347     207   2977 my( $self, $fileno, $mode ) = @_;
963              
964 272         379 if( ASSERT_DATA ) {
965             _trap "Illegal mode=$mode on fileno=$fileno" unless
966             $fileno == $signal_pipe_read_fd
967             and $mode eq MODE_RD;
968             }
969              
970             # Read all data from the signal pipe.
971             # The data itself doesn't matter.
972             # TODO - If writes can happen on the edge of @pending_signals (from
973             # 0 to 1 element), then we oughtn't need to loop here.
974              
975 272         2319 while (1) {
976 295         1474 my $octets_count = sysread( $signal_pipe_read, (my $data), 65536 );
977              
978 414 100       2002 next if $octets_count;
979 326 100       887 last if defined $octets_count;
980              
981 207 100 66     1391 last if $! == EAGAIN or $! == EWOULDBLOCK;
982              
983 119         1164 if (ASSERT_DATA) {
984             _trap " Error " . ($!+0) . " reading from signal pipe: $!";
985             }
986 0         0 elsif(TRACE_SIGNALS) {
987             _warn " Error " . ($!+0) . " reading from signal pipe: $!";
988             }
989              
990 0         0 last;
991             }
992              
993             # Double buffer signals.
994             # The intent is to avoid a race condition by processing the same
995             # buffer that new signals go into.
996              
997 88 100       271 return unless @pending_signals;
998 207         3064 my @signals = @pending_signals;
999 207         650 @pending_signals = ();
1000              
1001 207         371 if (TRACE_SIGNALS) {
1002             _warn " Read " . scalar(@signals) . " signals from the list";
1003             }
1004              
1005 207         373 foreach my $signal (@signals) {
1006 207         744 my $signal_name = $signal->[SIGINFO_NAME];
1007 207         1067 my $signal_src_pid = $signal->[SIGINFO_SRC_PID];
1008              
1009             # Ignore signals from other processes.
1010             # This can happen if we've fork()ed without calling has_forked()
1011             # to reset the signals subsystem.
1012             #
1013             # TODO - We might be able to get rid of has_forked() if PID
1014             # mismatches are detected.
1015              
1016 228 50       630 next if $signal_src_pid != $$;
1017              
1018 228 100       676 if( $signal_name eq 'CHLD' ) {
    100          
1019 228         805 _loop_signal_handler_chld_bottom( $signal_name );
1020             }
1021             elsif( $signal_name eq 'PIPE' ) {
1022 140         575 _loop_signal_handler_pipe_bottom( $signal_name );
1023             }
1024             else {
1025 37         164 _loop_signal_handler_generic_bottom( $signal_name );
1026             }
1027             }
1028             }
1029              
1030             1;
1031              
1032             __END__