1# Plain Perl signal handling is something shared by several event
2# loops.  The invariant code has moved out here so that each loop may
3# use it without reinventing it.  This will save maintenance and
4# shrink the distribution.  Yay!
5
6package POE::Loop::PerlSignals;
7
8use strict;
9
10use vars qw($VERSION);
11$VERSION = '1.368'; # NOTE - Should be #.### (three decimal places)
12
13# Everything plugs into POE::Kernel.
14package POE::Kernel;
15
16use strict;
17use POE::Kernel;
18
19# Flag so we know which signals are watched.  Used to reset those
20# signals during finalization.
21my %signal_watched;
22
23#------------------------------------------------------------------------------
24# Signal handlers/callbacks.
25
26sub _loop_signal_handler_generic {
27  if( USE_SIGNAL_PIPE ) {
28    POE::Kernel->_data_sig_pipe_send( $_[0] );
29  }
30  else {
31    _loop_signal_handler_generic_bottom( $_[0] );
32  }
33}
34
35sub _loop_signal_handler_generic_bottom {
36  if (TRACE_SIGNALS) {
37    POE::Kernel::_warn "<sg> Enqueuing generic SIG$_[0] event";
38  }
39
40  $poe_kernel->_data_ev_enqueue(
41    $poe_kernel, $poe_kernel, EN_SIGNAL, ET_SIGNAL, [ $_[0] ],
42    __FILE__, __LINE__, undef
43  );
44  $SIG{$_[0]} = \&_loop_signal_handler_generic;
45}
46
47##
48
49sub _loop_signal_handler_pipe {
50  if( USE_SIGNAL_PIPE ) {
51    POE::Kernel->_data_sig_pipe_send( $_[0] );
52  }
53  else {
54    _loop_signal_handler_pipe_bottom( $_[0] );
55  }
56}
57
58sub _loop_signal_handler_pipe_bottom {
59  if (TRACE_SIGNALS) {
60    POE::Kernel::_warn "<sg> Enqueuing PIPE-like SIG$_[0] event";
61  }
62
63  $poe_kernel->_data_ev_enqueue(
64    $poe_kernel, $poe_kernel, EN_SIGNAL, ET_SIGNAL, [ $_[0] ],
65    __FILE__, __LINE__, undef
66  );
67  $SIG{$_[0]} = \&_loop_signal_handler_pipe;
68}
69
70## only used under USE_SIGCHLD
71
72sub _loop_signal_handler_chld {
73  if( USE_SIGNAL_PIPE ) {
74    POE::Kernel->_data_sig_pipe_send( 'CHLD' );
75  }
76  else {
77    _loop_signal_handler_chld_bottom( $_[0] );
78  }
79}
80
81sub _loop_signal_handler_chld_bottom {
82  if (TRACE_SIGNALS) {
83    POE::Kernel::_warn "<sg> Enqueuing CHLD-like SIG$_[0] event";
84  }
85
86  $poe_kernel->_data_sig_enqueue_poll_event($_[0]);
87}
88
89#------------------------------------------------------------------------------
90# Signal handler maintenance functions.
91
92sub loop_watch_signal {
93  my ($self, $signal) = @_;
94
95  $signal_watched{$signal} = 1;
96
97  # Child process has stopped.
98  if ($signal eq 'CHLD' or $signal eq 'CLD') {
99    if ( USE_SIGCHLD ) {
100      # Poll once for signals.  Will set the signal handler when done.
101      # It would be more efficient to set $SIG{$signal} here and reap
102      # processes, but that would synchronously set the signal
103      # handler, and subsequent system() calls within the callback
104      # could fail with a -1 return value.  The polling event defers
105      # the setup until the current callback returns.
106      $self->_data_sig_enqueue_poll_event($signal);
107    } else {
108      # We should never twiddle $SIG{CH?LD} under POE, unless we want to
109      # override system() and friends. --hachi
110      # $SIG{$signal} = "DEFAULT";
111      $self->_data_sig_begin_polling($signal);
112    }
113    return;
114  }
115
116  # Broken pipe.
117  if ($signal eq 'PIPE') {
118    $SIG{$signal} = \&_loop_signal_handler_pipe;
119    return;
120  }
121
122  # Everything else.
123  $SIG{$signal} = \&_loop_signal_handler_generic;
124}
125
126sub loop_ignore_signal {
127  my ($self, $signal) = @_;
128
129  delete $signal_watched{$signal};
130
131  if ($signal eq 'CHLD' or $signal eq 'CLD') {
132    if ( USE_SIGCHLD ) {
133      if ($self->_data_sig_kernel_awaits_pids()) {
134        # We need SIGCHLD to stay around after shutdown, so that
135        # child processes may be reaped and kr_child_procs=0
136        if (TRACE_SIGNALS) {
137          POE::Kernel::_warn "<sg> Keeping SIG$signal anyway!";
138        }
139        return;
140      }
141    } else {
142      $self->_data_sig_cease_polling();
143      # We should never twiddle $SIG{CH?LD} under poe, unless we want to
144      # override system() and friends. --hachi
145      # $SIG{$signal} = "IGNORE";
146      return;
147    }
148  }
149
150  delete $signal_watched{$signal};
151
152  my $state = 'DEFAULT';
153  if ($signal eq 'PIPE') {
154    $state = "IGNORE";
155  }
156
157  if (TRACE_SIGNALS) {
158    POE::Kernel::_warn "<sg> $state SIG$signal";
159  }
160  $SIG{$signal} = $state;
161}
162
163sub loop_ignore_all_signals {
164  my $self = shift;
165  foreach my $signal (keys %signal_watched) {
166    $self->loop_ignore_signal($signal);
167  }
168}
169
1701;
171
172__END__
173
174=head1 NAME
175
176POE::Loop::PerlSignals - common signal handling routines for POE::Loop bridges
177
178=head1 SYNOPSIS
179
180See L<POE::Loop>.
181
182=head1 DESCRIPTION
183
184POE::Loop::PerlSignals implements common code to handle signals for
185many different event loops.  Most loops don't handle signals natively,
186so this code has been abstracted into a reusable mix-in module.
187
188POE::Loop::PerlSignals follows POE::Loop's public interface for signal
189handling.  Therefore, please see L<POE::Loop> for more details.
190
191=head1 SEE ALSO
192
193L<POE>, L<POE::Loop>
194
195=head1 AUTHORS & LICENSING
196
197Please see L<POE> for more information about authors, contributors,
198and POE's licensing.
199
200=cut
201
202# rocco // vim: ts=2 sw=2 expandtab
203# TODO - Edit.
204