POE

 view release on metacpan or  search on metacpan

lib/POE/Resource/Signals.pm  view on Meta::CPAN

use vars qw($VERSION);
$VERSION = '1.370'; # NOTE - Should be #.### (three decimal places)

# These methods are folded into POE::Kernel;
package POE::Kernel;

use strict;

use POE::Pipe::OneWay;
use POE::Resource::FileHandles;
use POSIX qw(:sys_wait_h sigprocmask SIG_SETMASK);

### Map watched signal names to the sessions that are watching them
### and the events that must be delivered when they occur.

sub SEV_EVENT   () { 0 }
sub SEV_ARGS    () { 1 }
sub SEV_SESSION () { 2 }

my %kr_signals;
#  ( $signal_name =>
#    { $session_id =>
#     [ $event_name,    SEV_EVENT
#       $event_args,    SEV_ARGS
#       $session_ref,   SEV_SESSION
#     ],
#      ...,
#    },
#    ...,
#  );

my %kr_sessions_to_signals;
#  ( $session_id =>
#    { $signal_name =>
#      [ $event_name,   SEV_EVENT
#        $event_args,   SEV_ARGS
#        $session_ref,  SEV_SESSION
#      ],
#      ...,
#    },
#    ...,
#  );

my %kr_pids_to_events;
# { $pid =>
#   { $session_id =>
#     [ $blessed_session,   # PID_SESSION
#       $event_name,        # PID_EVENT
#       $args,              # PID_ARGS
#     ]
#   }
# }

my %kr_sessions_to_pids;
# { $session_id => { $pid => 1 } }

sub PID_SESSION () { 0 }
sub PID_EVENT   () { 1 }
sub PID_ARGS    () { 2 }

sub _data_sig_relocate_kernel_id {
  my ($self, $old_id, $new_id) = @_;

  while (my ($signal, $sig_rec) = each %kr_signals) {
    next unless exists $sig_rec->{$old_id};
    $sig_rec->{$new_id} = delete $sig_rec->{$old_id};
  }

  $kr_sessions_to_signals{$new_id} = delete $kr_sessions_to_signals{$old_id}
    if exists $kr_sessions_to_signals{$old_id};

  while (my ($pid, $pid_rec) = each %kr_pids_to_events) {
    next unless exists $pid_rec->{$old_id};
    $pid_rec->{$new_id} = delete $pid_rec->{$old_id};
  }

  $kr_sessions_to_pids{$new_id} = delete $kr_sessions_to_pids{$old_id}
    if exists $kr_sessions_to_pids{$old_id};
}

# Bookkeeping per dispatched signal.

# TODO - Why not lexicals?
use vars (
 '@kr_signaled_sessions',            # The sessions touched by a signal.
 '$kr_signal_total_handled',         # How many sessions handled a signal.
 '$kr_signal_type',                  # The type of signal being dispatched.
);

#my @kr_signaled_sessions;           # The sessions touched by a signal.
#my $kr_signal_total_handled;        # How many sessions handled a signal.
#my $kr_signal_type;                 # The type of signal being dispatched.

# A flag to tell whether we're currently polling for signals.
# Under USE_SIGCHLD, determines whether a SIGCHLD polling event has
# already been queued.
my $polling_for_signals = 0;

# There may be latent subprocesses in some environments.
# Or we may need to "always loop once" if we're polling for SIGCHLD.
# This constant lets us define those exceptional cases.
# We had some in the past, but as of 2013-10-06 we seem to have
# eliminated those special cases.
use constant BASE_SIGCHLD_COUNT => 0;

my $kr_has_child_procs = BASE_SIGCHLD_COUNT;

# A list of special signal types.  Signals that aren't listed here are
# benign (they do not kill sessions at all).  "Terminal" signals are
# the ones that UNIX defaults to killing processes with.  Thus STOP is
# not terminal.

sub SIGTYPE_BENIGN      () { 0x00 }
sub SIGTYPE_TERMINAL    () { 0x01 }
sub SIGTYPE_NONMASKABLE () { 0x02 }

my %_signal_types = (
  QUIT => SIGTYPE_TERMINAL,
  INT  => SIGTYPE_TERMINAL,
  KILL => SIGTYPE_TERMINAL,
  TERM => SIGTYPE_TERMINAL,

lib/POE/Resource/Signals.pm  view on Meta::CPAN

  my $mask_temp = POSIX::SigSet->new();
  sigprocmask( SIG_SETMASK, $signal_mask_all, $mask_temp )
            or _trap "<sg> Unable to mask all signals: $!";
}

### Unmask all signals
sub _data_sig_unmask_all {
  return if RUNNING_IN_HELL;
  my $self = $poe_kernel;
  unless( $signal_mask_none ) {
    $self->_data_sig_mask_build;
  }
  my $mask_temp = POSIX::SigSet->new();
  sigprocmask( SIG_SETMASK, $signal_mask_none, $mask_temp )
        or _trap "<sg> Unable to unmask all signals: $!";
}



sub _data_sig_pipe_finalize {
  my( $self ) = @_;
  if( $signal_pipe_read ) {
    $self->loop_ignore_filehandle( $signal_pipe_read, MODE_RD );
    close $signal_pipe_read; undef $signal_pipe_read;
  }
  if( $signal_pipe_write ) {
    close $signal_pipe_write; undef $signal_pipe_write;
  }
  # Don't send anything more!
  undef( $signal_pipe_pid );
}

### Send a signal "message" to the main thread
### Called from the top signal handlers
sub _data_sig_pipe_send {
  local $!;

  my $signal_name = $_[1];

  if( TRACE_SIGNALS ) {
    _warn "<sg> Caught SIG$signal_name";
  }

  return if $finalizing;

  if( not defined $signal_pipe_pid ) {
    _trap "<sg> _data_sig_pipe_send called before signal pipe was initialized.";
  }

  # ugh- has_forked() can't be called fast enough.  This warning might
  # show up before it is called.  Should we just detect forking and do it
  # for the user?  Probably not...

  if( $$ != $signal_pipe_pid ) {
    _warn(
      "<sg> Signal caught in different process than POE::Kernel initialized " .
      "(newPID=$$ oldPID=$signal_pipe_pid sig=$signal_name).\n"
    );
    _warn(
      "Call POE::Kernel->has_forked() in the child process " .
      "to relocate the signal handler.\n"
    );
  }

  # We're registering signals in a list.  Pipes have more finite
  # capacity, so we'll just write a single-byte semaphore-like token.
  # It's up to the reader to process the list.  Duplicates are
  # permitted, and their ordering may be significant.  Precedent:
  # http://search.cpan.org/perldoc?IPC%3A%3AMorseSignals

  push @pending_signals, [
    $signal_name, # SIGINFO_NAME
    $$,           # SIGINFO_SRC_PID
  ];

  if (TRACE_SIGNALS) {
    _warn "<sg> Attempting signal pipe write";
  }

  my $count = syswrite( $signal_pipe_write, '!' );

  # TODO - We need to crash gracefully if the write fails, but not if
  # it's due to the pipe being full.  We might solve this by only
  # writing on the edge of @pending_signals == 1 after the push().
  # We assume @pending_signals > 1 means there's a byte in the pipe,
  # so the reader will wake up to catch 'em all.

  if ( ASSERT_DATA ) {
    unless (defined $count and $count == 1) {
      _trap "<sg> Signal pipe write failed: $!";
    }
  }
}

### Read all signal numbers.
### Call the related bottom handler.  That is, inside the kernel loop.
sub _data_sig_pipe_read {
  my( $self, $fileno, $mode ) = @_;

  if( ASSERT_DATA ) {
    _trap "Illegal mode=$mode on fileno=$fileno" unless
                                    $fileno == $signal_pipe_read_fd
                                and $mode eq MODE_RD;
  }

  # Read all data from the signal pipe.
  # The data itself doesn't matter.
  # TODO - If writes can happen on the edge of @pending_signals (from
  # 0 to 1 element), then we oughtn't need to loop here.

  while (1) {
    my $octets_count = sysread( $signal_pipe_read, (my $data), 65536 );

    next if $octets_count;
    last if defined $octets_count;

    last if $! == EAGAIN or $! == EWOULDBLOCK;

    if (ASSERT_DATA) {
      _trap "<sg> Error " . ($!+0) . " reading from signal pipe: $!";
    }



( run in 0.490 second using v1.01-cache-2.11-cpan-71847e10f99 )