AnyEvent-FDpasser

 view release on metacpan or  search on metacpan

lib/AnyEvent/FDpasser.pm  view on Meta::CPAN

  delete $self->{fh_pair};
}




sub push_send_fh {
  my ($self, $fh_to_send, $cb) = @_;

  die "passer object is in error_state: $self->{error_state}" if exists $self->{error_state};
  die "must call i_am_parent or i_am_child" if exists $self->{fh_pair};

  $cb ||= sub {};

  push @{$self->{obuf}}, [$fh_to_send, $cb];

  $self->try_to_send;
}


sub push_recv_fh {
  my ($self, $cb) = @_;

  die "passer object is in error_state: $self->{error_state}" if exists $self->{error_state};
  die "must call i_am_parent or i_am_child" if exists $self->{fh_pair};

  push @{$self->{ibuf}}, $cb;

  $self->try_to_recv;
}




sub try_to_send {
  my ($self) = @_;

  return unless $self->{fh};
  return unless @{$self->{obuf}};
  return if defined $self->{owatcher};
  return if defined $self->{full_descriptor_table_state};

  $self->{owatcher} = AE::io $self->{fh}, 1, sub {

    my $fh_to_send = shift @{$self->{obuf}};

    my $rv = send_fd(fileno($self->{fh}), fileno($fh_to_send->[0]));

    if ($rv < 0) {
      if ($!{EAGAIN} || $!{EWOULDBLOCK} || $!{EINTR}) {
        ## Spurious ready notification or signal: put fh back on queue
        unshift @{$self->{obuf}}, $fh_to_send;
      } else {
        ## Unknown error
        $self->error($!);
      }
    } elsif ($rv == 0) {
      $self->error('sendmsg wrote 0 bytes');
    } else {
      $fh_to_send->[1]->();
      ## Don't do a close($fh_to_send->[0]) because the program may wish to keep it alive
      undef $fh_to_send;
      $self->{owatcher} = undef;
      $self->try_to_send;
    }

  };
}


sub try_to_recv {
  my ($self) = @_;

  return unless @{$self->{ibuf}};
  return if defined $self->{iwatcher};
  return if defined $self->{full_descriptor_table_state};

  $self->{iwatcher} = AE::io $self->{fh}, 0, sub {

    my $cb = shift @{$self->{ibuf}};

    POSIX::close($self->{fh_duped});
    delete $self->{fh_duped};

    ## Race condition: If another thread or a signal handler creates a new descriptor at this
    ## exact point in time, it could cause the descriptor table to fill up and the following
    ## to error.

    my $rv = recv_fd(fileno($self->{fh}));

    if ($rv == -1) {
      if ($!{EAGAIN} || $!{EWOULDBLOCK} || $!{EINTR}) {
        ## Spurious ready notification or signal: put the cb back on the queue
        unshift @{$self->{ibuf}}, $cb;
      } elsif ($!{EMSGSIZE} || $!{EMFILE} || $!{ENFILE}) {
        ## File descriptor table is full. This should be very unlikely given the close+duping
        ## technique used to detect this. In this case the descriptor stream may be
        ## desynchronised and we must shutdown the passer.

        my $err = $!;

        carp "AnyEvent::FDpasser - file descriptor table full, closing passer: $!";

        $self->error($err);
      } else {
        ## Unknown error
        $self->error($!);
      }
    } elsif ($rv == -2) {
      $self->error("cmsg truncated");
    } elsif ($rv == 0) {
      ## Orderly shutdown
      $self->error(undef);
    } else {
      open(my $new_fh, '+<&=', $rv);
      $self->{iwatcher} = undef;
      $cb->($new_fh);
      $self->try_to_recv;
    }
  };



( run in 0.620 second using v1.01-cache-2.11-cpan-df04353d9ac )