home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-perl-addon-1.4.9-installer.exe / Signals.pm < prev    next >
Encoding:
Perl POD Document  |  2004-01-21  |  9.4 KB  |  345 lines

  1. # $Id: Signals.pm,v 1.10 2004/01/21 05:28:15 rcaputo Exp $
  2.  
  3. # The data necessary to manage signals, and the accessors to get at
  4. # that data in a sane fashion.
  5.  
  6. package POE::Resources::Signals;
  7.  
  8. use vars qw($VERSION);
  9. $VERSION = do {my@r=(q$Revision: 1.10 $=~/\d+/g);sprintf"%d."."%04d"x$#r,@r};
  10.  
  11. # These methods are folded into POE::Kernel;
  12. package POE::Kernel;
  13.  
  14. use strict;
  15.  
  16. ### Map watched signal names to the sessions that are watching them
  17. ### and the events that must be delivered when they occur.
  18.  
  19. my %kr_signals;
  20. #  ( $signal_name =>
  21. #    { $session_reference => $event_name,
  22. #      ...,
  23. #    },
  24. #    ...,
  25. #  );
  26.  
  27. my %kr_sessions_to_signals;
  28. #  ( $session =>
  29. #    { $signal_name => $event_name,
  30. #      ...,
  31. #    },
  32. #    ...,
  33. #  );
  34.  
  35. # Bookkeeping per dispatched signal.
  36.  
  37. my @kr_signaled_sessions;           # The sessions touched by a signal.
  38. my $kr_signal_total_handled;        # How many sessions handled a signal.
  39. my $kr_signal_handled_implicitly;   # Whether it was handled implicitly.
  40. my $kr_signal_handled_explicitly;   # Whether it was handled explicitly.
  41. my $kr_signal_type;                 # The type of signal being dispatched.
  42.  
  43. sub _data_sig_preload {
  44.   $poe_kernel->[KR_SIGNALS] = \%kr_signals;
  45. }
  46. use POE::API::ResLoader \&_data_sig_preload;
  47.  
  48. # A list of special signal types.  Signals that aren't listed here are
  49. # benign (they do not kill sessions at all).  "Terminal" signals are
  50. # the ones that UNIX defaults to killing processes with.  Thus STOP is
  51. # not terminal.
  52.  
  53. sub SIGTYPE_BENIGN      () { 0x00 }
  54. sub SIGTYPE_TERMINAL    () { 0x01 }
  55. sub SIGTYPE_NONMASKABLE () { 0x02 }
  56.  
  57. my %_signal_types =
  58.   ( QUIT => SIGTYPE_TERMINAL,
  59.     INT  => SIGTYPE_TERMINAL,
  60.     KILL => SIGTYPE_TERMINAL,
  61.     TERM => SIGTYPE_TERMINAL,
  62.     HUP  => SIGTYPE_TERMINAL,
  63.     IDLE => SIGTYPE_TERMINAL,
  64.     ZOMBIE    => SIGTYPE_NONMASKABLE,
  65.     UIDESTROY => SIGTYPE_NONMASKABLE,
  66.   );
  67.  
  68. # Build a list of useful, real signals.  Nonexistent signals, and ones
  69. # which are globally unhandled, usually cause segmentation faults if
  70. # perl was poorly configured.  Some signals aren't available in some
  71. # environments.
  72.  
  73. my @_safe_signals;
  74.  
  75. sub _data_sig_initialize {
  76.   my $self = shift;
  77.  
  78.   # In case we're called multiple times.
  79.   unless (@_safe_signals) {
  80.     foreach my $signal (keys %SIG) {
  81.  
  82.       # Nonexistent signals, and ones which are globally unhandled.
  83.       next if ($signal =~ /^( NUM\d+
  84.                               |__[A-Z0-9]+__
  85.                               |ALL|CATCHALL|DEFER|HOLD|IGNORE|MAX|PAUSE
  86.                               |RTMIN|RTMAX|SETS
  87.                               |SEGV
  88.                               |
  89.                             )$/x
  90.               );
  91.  
  92.       # Windows doesn't have a SIGBUS, but the debugger causes SIGBUS
  93.       # to be entered into %SIG.  It's fatal to register its handler.
  94.       next if $signal eq 'BUS' and RUNNING_IN_HELL;
  95.  
  96.       # Apache uses SIGCHLD and/or SIGCLD itself, so we can't.
  97.       next if $signal =~ /^CH?LD$/ and exists $INC{'Apache.pm'};
  98.  
  99.       push @_safe_signals, $signal;
  100.     }
  101.   }
  102.  
  103.   # Regsiter handlers for all safe signals.
  104.   foreach (@_safe_signals) {
  105.     $self->loop_watch_signal($_);
  106.   }
  107. }
  108.  
  109. ### Return signals that are safe to manipulate.
  110.  
  111. sub _data_sig_get_safe_signals {
  112.   return @_safe_signals;
  113. }
  114.  
  115. ### End-run leak checking.
  116.  
  117. sub _data_sig_finalize {
  118.   my $finalized_ok = 1;
  119.  
  120.   while (my ($sig, $sig_rec) = each(%kr_signals)) {
  121.     $finalized_ok = 0;
  122.     _warn "!!! Leaked signal $sig\n";
  123.     while (my ($ses, $event) = each(%{$kr_signals{$sig}})) {
  124.       _warn "!!!\t$ses = $event\n";
  125.     }
  126.   }
  127.  
  128.   while (my ($ses, $sig_rec) = each(%kr_sessions_to_signals)) {
  129.     $finalized_ok = 0;
  130.     _warn "!!! Leaked signal cross-reference: $ses\n";
  131.     while (my ($sig, $event) = each(%{$kr_signals{$ses}})) {
  132.       _warn "!!!\t$sig = $event\n";
  133.     }
  134.   }
  135.  
  136.   return $finalized_ok;
  137. }
  138.  
  139. ### Add a signal to a session.
  140.  
  141. sub _data_sig_add {
  142.   my ($self, $session, $signal, $event) = @_;
  143.   $kr_sessions_to_signals{$session}->{$signal} = $event;
  144.   $kr_signals{$signal}->{$session} = $event;
  145. }
  146.  
  147. ### Remove a signal from a session.
  148.  
  149. sub _data_sig_remove {
  150.   my ($self, $session, $signal) = @_;
  151.  
  152.   delete $kr_sessions_to_signals{$session}->{$signal};
  153.   delete $kr_sessions_to_signals{$session}
  154.     unless keys(%{$kr_sessions_to_signals{$session}});
  155.  
  156.   delete $kr_signals{$signal}->{$session};
  157.   delete $kr_signals{$signal} unless keys %{$kr_signals{$signal}};
  158. }
  159.  
  160. ### Clear all the signals from a session.
  161.  
  162. sub _data_sig_clear_session {
  163.   my ($self, $session) = @_;
  164.   return unless exists $kr_sessions_to_signals{$session}; # avoid autoviv
  165.   foreach (keys %{$kr_sessions_to_signals{$session}}) {
  166.     $self->_data_sig_remove($session, $_);
  167.   }
  168. }
  169.  
  170. ### Return a signal's type, or SIGTYPE_BENIGN if it's not special.
  171.  
  172. sub _data_sig_type {
  173.   my ($self, $signal) = @_;
  174.   return $_signal_types{$signal} || SIGTYPE_BENIGN;
  175. }
  176.  
  177. ### Flag a signal as being handled by some session.
  178.  
  179. sub _data_sig_handled {
  180.   my $self = shift;
  181.   $kr_signal_total_handled = 1;
  182.   $kr_signal_handled_explicitly = 1;
  183. }
  184.  
  185. ### Clear the structures associated with a signal's "handled" status.
  186.  
  187. sub _data_sig_reset_handled {
  188.   my ($self, $signal) = @_;
  189.   undef $kr_signal_total_handled;
  190.   $kr_signal_type = $self->_data_sig_type($signal);
  191.   undef @kr_signaled_sessions;
  192. }
  193.  
  194. ### Is the signal explicitly watched?
  195.  
  196. sub _data_sig_explicitly_watched {
  197.   my ($self, $signal) = @_;
  198.   return exists $kr_signals{$signal};
  199. }
  200.  
  201. ### Return the signals watched by a session and the events they
  202. ### generate.  -><- Used mainly for testing, but may also be useful
  203. ### for introspection.
  204.  
  205. sub _data_sig_watched_by_session {
  206.   my ($self, $session) = @_;
  207.   return %{$kr_sessions_to_signals{$session}};
  208. }
  209.  
  210. ### Which sessions are watching a signal?
  211.  
  212. sub _data_sig_watchers {
  213.   my ($self, $signal) = @_;
  214.   return %{$kr_signals{$signal}};
  215. }
  216.  
  217. ### Return the current signal's handled status.  -><- Used for
  218. ### testing.
  219.  
  220. sub _data_sig_handled_status {
  221.   return(
  222.     $kr_signal_handled_explicitly,
  223.     $kr_signal_handled_implicitly,
  224.     $kr_signal_total_handled,
  225.     $kr_signal_type,
  226.     \@kr_signaled_sessions,
  227.   );
  228. }
  229.  
  230. ### Determine if a given session is watching a signal.  This uses a
  231. ### two-step exists so that the longer one does not autovivify keys in
  232. ### the shorter one.
  233.  
  234. sub _data_sig_is_watched_by_session {
  235.   my ($self, $signal, $session) = @_;
  236.   return(
  237.     exists($kr_signals{$signal}) &&
  238.     exists($kr_signals{$signal}->{$session})
  239.   );
  240. }
  241.  
  242. ### Clear the flags that determine if/how a session handled a signal.
  243.  
  244. sub _data_sig_clear_handled_flags {
  245.   undef $kr_signal_handled_implicitly;
  246.   undef $kr_signal_handled_explicitly;
  247. }
  248.  
  249. ### Destroy sessions touched by a nonmaskable signal or by an
  250. ### unhandled terminal signal.  Check for garbage-collection on
  251. ### sessions which aren't to be terminated.
  252.  
  253. sub _data_sig_free_terminated_sessions {
  254.   my $self = shift;
  255.  
  256.   if (
  257.     ($kr_signal_type & SIGTYPE_NONMASKABLE) or
  258.     ($kr_signal_type & SIGTYPE_TERMINAL and !$kr_signal_total_handled)
  259.   ) {
  260.     foreach my $dead_session (@kr_signaled_sessions) {
  261.       next unless $self->_data_ses_exists($dead_session);
  262.       if (TRACE_SIGNALS) {
  263.         _warn(
  264.           "<sg> stopping signaled session ",
  265.           $self->_data_alias_loggable($dead_session)
  266.         );
  267.       }
  268.  
  269.       $self->_data_ses_stop($dead_session);
  270.     }
  271.   }
  272.   else {
  273.     # -><- Implicit signal reaping.  This is deprecated behavior and
  274.     # will eventually be removed.  See the commented out tests in
  275.     # t/res/signals.t.
  276.     foreach my $touched_session (@kr_signaled_sessions) {
  277.       next unless $self->_data_ses_exists($touched_session);
  278.       $self->_data_ses_collect_garbage($touched_session);
  279.     }
  280.   }
  281.  
  282.   # Erase @kr_signaled_sessions, or they will leak until the next
  283.   # signal.
  284.   undef @kr_signaled_sessions;
  285. }
  286.  
  287. ### A signal has touched a session.  Record this fact for later
  288. ### destruction tests.
  289.  
  290. sub _data_sig_touched_session {
  291.   my ($self, $session, $event, $handler_retval, $signal) = @_;
  292.  
  293.   push @kr_signaled_sessions, $session;
  294.   $kr_signal_total_handled      += !!$handler_retval;
  295.   $kr_signal_handled_implicitly += !!$handler_retval;
  296.  
  297.   unless ($kr_signal_handled_explicitly) {
  298.     if ($kr_signal_handled_implicitly) {
  299.       _die(
  300.         ",----- DEPRECATION ERROR -----\n",
  301.         "| Session ", $self->_data_alias_loggable($session), ":\n",
  302.         "| handled SIG$signal by returning a true value.\n",
  303.         "| You must use sig_handled() if this was intentional, or ensure.\n",
  304.         "| that the signal handler returns a false value.  If this message\n",
  305.         "| is generated by a third-party component, please upgrade it or\n",
  306.         "| contact its author.\n",
  307.         "`-----------------------------\n",
  308.       );
  309.     }
  310.   }
  311. }
  312.  
  313. 1;
  314.  
  315. __END__
  316.  
  317. =head1 NAME
  318.  
  319. POE::Resources::Signals - signal management for POE::Kernel
  320.  
  321. =head1 SYNOPSIS
  322.  
  323. Used internally by POE::Kernel.  Better documentation will be
  324. forthcoming.
  325.  
  326. =head1 DESCRIPTION
  327.  
  328. This module encapsulates and provides accessors for POE::Kernel's data
  329. structures that manage signals.  It is used internally by POE::Kernel
  330. and has no public interface.
  331.  
  332. =head1 SEE ALSO
  333.  
  334. See L<POE::Kernel> for documentation on signals.
  335.  
  336. =head1 BUGS
  337.  
  338. Probably.
  339.  
  340. =head1 AUTHORS & COPYRIGHTS
  341.  
  342. Please see L<POE> for more information about authors and contributors.
  343.  
  344. =cut
  345.