package Coro::ProcessPool::Worker;
# ABSTRACT: Worker class that runs in the child process
$Coro::ProcessPool::Worker::VERSION = '0.26_002'; # TRIAL

$Coro::ProcessPool::Worker::VERSION = '0.26002';use Moo;
use Types::Standard qw(-types);
use AnyEvent;
use Carp;
use Coro;
use Coro::Handle;
use Coro::ProcessPool::Util qw($EOL decode encode);
use Module::Load qw(load);
use Devel::StackTrace;

has queue => (
  is      => 'ro',
  isa     => InstanceOf['Coro::Channel'],
  default => sub { Coro::Channel->new() },
);

has input => (
  is      => 'ro',
  isa     => InstanceOf['Coro::Handle'],
  default => sub { unblock(*STDIN) },
);

has input_monitor => (
  is  => 'lazy',
  isa => InstanceOf['Coro'],
);

sub _build_input_monitor {
  return async {
    my $self = shift;
    while (my $line = $self->input->readline($EOL)) {
      my ($id, $task, $args) = decode($line);
      $self->queue->put([$id, $task, $args]);
    }
  } @_;
}

has completed => (
  is      => 'ro',
  isa     => InstanceOf['Coro::Channel'],
  default => sub { Coro::Channel->new() },
);

has output => (
  is      => 'ro',
  isa     => InstanceOf['Coro::Handle'],
  default => sub { unblock(*STDOUT) },
);

has output_monitor => (
  is  => 'lazy',
  isa => InstanceOf['Coro'],
);

sub _build_output_monitor {
  return async {
    my $self = shift;
    while (my $data = $self->completed->get) {
      $self->output->print(encode(@$data) . $EOL);
    }
  } @_;
}

before run => sub {
  my $self = shift;
  $self->input_monitor;
  $self->output_monitor;
};

sub process_task {
  my ($class, $task, $args) = @_;

  my $result = eval {
    if (ref $task && ref $task eq 'CODE') {
      $task->(@$args);
    } else {
      load $task;
      die "method new() not found for class $task" unless $task->can('new');
      die "method run() not found for class $task" unless $task->can('run');
      my $obj = $task->new(@$args);
      $obj->run;
    }
  };

  if ($@) {
    my $error = $@;
    my $trace = Devel::StackTrace->new(
      message      => $error,
      indent       => 1,
      ignore_class => ['Coro::ProcessPool::Util', 'Coro', 'AnyEvent'],
    );
    return (1, $trace->as_string);
  }

  return (0, $result);
}

sub run {
  my $self = shift;
  $self->output->print($$ . $EOL);

  while (1) {
    my $job = $self->queue->get or last;
    my ($id, $task, $args) = @$job;
    last if $task eq 'self-terminate';
    my ($error, $result) = $self->process_task($task, $args);
    $self->completed->put([$id, $error, $result]);
  }

  exit 0;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Coro::ProcessPool::Worker - Worker class that runs in the child process

=head1 VERSION

version 0.26_002

=head1 AUTHOR

Jeff Ober <sysread@fastmail.fm>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Jeff Ober.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
