Async-Simple-Pool

 view release on metacpan or  search on metacpan

lib/Async/Simple/Task/Fork.pm  view on Meta::CPAN

Possible keys for %all_optional_params:

    task         => coderef, function, called for each "data" passed to child process via $task->put( $data );

    timeout      => timeout in seconds between child checkings for new data passed. default 0.01

    kill_on_exit => kill (1) or not (0) subprocess on object destroy (1 by default).

=cut


=head2 BUILD

internal. Some tricks here:)

    1. Master process called $task->new with fork() inside
    2. After forking done we have two processes:
    2.1. Master gets one side of reader/writer pipes and pid of child
    2.2. Child - another side of pipes and extra logic with everlasting loop

=cut

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

    # Return for master process
    # Only child tasks must go down and make a loop
    return $self  if $self->pid;

    # Child loop: untill parent is alive
    while ( 1 ) {
        $self->clear_answer;
        $self->get;

        unless ( $self->has_answer ) {
            sleep $self->timeout;
            next;
        }

        my $result = eval{ $self->task->( $self->answer ) };
        $self->clear_answer;
        $self->put( $result // $@ // '' );
    }

    exit(0);
};


=head2 fork_child

Makes child process and returns pid of child process to parent or 0 to child process

=cut

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

    # This is here instead of BEGIN, because this package uses as "extends" in Async::Simple::Task::ForkTmpFile
    # TODO: Maybe it would be great to move this code(function) to separate package
    # if ( $^O =~ /^(dos|os2|MSWin32|NetWare)$/ ) {
    #     die 'Your OS does not support threads... Use Async::Simple::Task::ForkTmpFile instead.';
    # };

    # Pipes: parent -> child and child -> parent
    pipe my( $parent_reader, $child_writer  )  or die 'Child  to Parent pipe open error';
    pipe my( $child_reader,  $parent_writer )  or die 'Parent to Child  pipe open error';

    my $pid = fork() // die "fork() failed: $!";

    # child
    unless ( $pid ) {
        close $parent_reader;
        close $parent_writer;

        $child_writer->autoflush(1);

        $self->writer( $child_writer );
        $self->reader( $child_reader );

        # Important!
        # Just after that we trap into BUILD
        # with the infinitive loop for child process (pid=0)
        return 0;
    }

    # parent
    close $child_writer;
    close $child_reader;

    $parent_writer->autoflush(1);

    $self->writer( $parent_writer );
    $self->reader( $parent_reader );

    return $pid;
};


=head2 get

Reads from task, if something can be readed or returns undef after timeout.

    my $result = $task->get;

Please note! If your function can return an undef value, then you shoud check

    $task->has_result.

=cut

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

    my $pipe = $self->reader;
    my $data;

    # Try to read "marker" into data within timeout
    # Each pack starts with an empty line and serialized string of useful data.
    eval {
        local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
        alarm $self->timeout;



( run in 0.649 second using v1.01-cache-2.11-cpan-b9db842bd85 )