AI-Evolve-Befunge

 view release on metacpan or  search on metacpan

lib/AI/Evolve/Befunge/Migrator.pm  view on Meta::CPAN

package AI::Evolve::Befunge::Migrator;
use strict;
use warnings;

use Carp;
use IO::Select;
use IO::Socket::INET;
use Perl6::Export::Attrs;
use POSIX qw(sysconf _SC_OPEN_MAX);

use AI::Evolve::Befunge::Util;


=head1 NAME

    AI::Evolve::Befunge::Migrator - connection to migration server


=head1 SYNOPSIS

    my $migrator = AI::Evolve::Befunge::Migrator->new(Local => $socket);
    $migrator->spin() while $migrator->alive();


=head1 DESCRIPTION

Maintains a connection to the migration server, migrationd.  This
module is meant to run in a child process, which will die when the
Local socket is closed.

It provides a non-blocking, fault tolerant adaptation layer between
migrationd (which may be somewhere across the internet) and the
AI::Evolve::Befunge population object (which spends most of its time
evolving critters, and only occasionally polls us).


=head1 CONSTRUCTOR

=head2 new

    my $migrator = AI::Evolve::Befunge::Migrator->new(Local => $socket);

Construct a new Migrator object.

The Local parameter is mandatory, it is the socket (typically a UNIX
domain socket) used to pass critters to and from the parent process.

Note that you probably don't want to call this directly... in most
cases you should call spawn_migrator, see below.

=cut

sub new {
    my ($package, %args) = @_;
    croak("The 'Local' parameter is required!") unless exists $args{Local};
    my $host = global_config('migrationd_host', 'quack.glines.org');
    my $port = global_config('migrationd_port', 29522);
    my $self = {
        host  => $host,
        port  => $port,
        dead  => 0,
        loc   => $args{Local},
        rxbuf => '',
        txbuf => '',
        lastc => 0,
    };
    return bless($self, $package);
}


=head2 spawn_migrator

    my $socket = spawn_migrator($config);

Spawn off an external migration child process.  This process will live
as long as the returned socket lives; it will die when the socket is
closed.  See AI::Evolve::Befunge::Migrator for implementation details.

=cut

sub spawn_migrator :Export(:DEFAULT) {
    my ($sock1, $sock2) = IO::Socket->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
    my $pid = fork();
    if($pid) {
        close($sock2);
        return $sock1;
    }

    close($sock1);
    for my $fd (0..sysconf(_SC_OPEN_MAX)-1) {
        next if $fd == $sock2->fileno();
        next if $fd == STDERR->fileno();
        POSIX::close($fd);
    }
    $sock2->blocking(0);
    my $migrator = AI::Evolve::Befunge::Migrator->new(Local  => $sock2);
    $migrator->spin() while $migrator->alive();
    exit(0);
}


=head1 METHODS

=head2 spin

    $migrator->spin();

This is the main control component of this module.  It looks for
incoming events and responds to them.

=cut

sub spin {
    my $self = shift;
    $self->spin_reads();
    $self->spin_writes();
    $self->spin_exceptions();



( run in 2.091 seconds using v1.01-cache-2.11-cpan-5a3173703d6 )