Práctica: Marshalling: Modifique Proc::Simple

Estudie el módulo Proc::Simple. Este es un ejemplo de uso:
use Proc::Simple;

$| = 1;                                 # debuffer output
$max_parallel_jobs = 5;                 # jobs processed in parallel
@running = ();                          # array of running jobs

foreach $job (1..9) {                   # create pseudo jobs
    push(@todo, "sleep 3"); 
}                                       

######################################################################
                                        # while there are jobs to do
while($#todo >= 0 || $#running >= 0) {  # or started ones are running
    @running = grep { $_->poll() } @running;  # remove finished jobs

    if($#running + 1 < $max_parallel_jobs &&  # space free in running?
       defined($job = pop(@todo))) {          # ... and job available

        print "Starting job '$job' ... ";
        $proc = Proc::Simple->new();    # new process
        $proc->start($job) || die "Cannot start job $job";
        push(@running, $proc);          # include in running list
    
        print "STARTED. (Remaining: ", $#todo+1, 
              " Running: ", $#running + 1, ")\n";
        next;                           # proceed without delay
    }
    sleep(1);                           # pause ... and proceed
}
Cuando se ejecuta, produce esta salida:
$ perl eg/parproc.pl 
Starting job 'sleep 3' ... STARTED. (Remaining: 8 Running: 1)
Starting job 'sleep 3' ... STARTED. (Remaining: 7 Running: 2)
Starting job 'sleep 3' ... STARTED. (Remaining: 6 Running: 3)
Starting job 'sleep 3' ... STARTED. (Remaining: 5 Running: 4)
Starting job 'sleep 3' ... STARTED. (Remaining: 4 Running: 5)
Starting job 'sleep 3' ... STARTED. (Remaining: 3 Running: 5)
Starting job 'sleep 3' ... STARTED. (Remaining: 2 Running: 5)
Starting job 'sleep 3' ... STARTED. (Remaining: 1 Running: 3)
Starting job 'sleep 3' ... STARTED. (Remaining: 0 Running: 4)
Basándose en Proc::Simple escriba un módulo Proc::RPC que de funcionalidades similares:

Posibles Consideraciones:

Nueva funcionalidad: Remote Procedure Calls. Se propone modificar el código de start y añadir un método join similar a wait pero que para los procesos hijo que ejecutan código Perl retorna una estructura de datos Perl. Usando la API de Proc::Simple::Async podría ser utilizada así:
    $proc = async { map { $_ * $_ } @_  } , 1..3;
    ...
    my @r = $proc->join(); # @r es ahora (1, 4, 9)
Puede usar Data::Dumper o Storable para serializar los datos retornados. Para ello deberá modificar el método start de Proc::Simple:
sub start {
  my $self  = shift;
  my ($func, @params) = @_;

  # Reap Zombies automatically
  $SIG{'CHLD'} = \&THE_REAPER;

  # Fork a child process
  $self->{'pid'} = fork();
  return 0 unless defined $self->{'pid'};  #   return Error if fork failed

  if($self->{pid} == 0) { # Child

      if (defined $self->{'redirect_stderr'}) { ...  }

      if (defined $self->{'redirect_stdout'}) { ...  }

      if(ref($func) eq "CODE") {
        my @r = $func->(@params); 
        #  Serialize @r in a temp file using Data::Dumper o Storable u otro ...
        $self->serialize(@r);
        exit 0;            # Start perl subroutine
      } else {
          exec $func, @params;       # Start shell process
          exit 0;                    # In case something goes wrong
      }
  } elsif($self->{'pid'} > 0) {      # Parent:
      ...
  } else {      
      return 0;                      #   this shouldn't occur
  }
}
El método join funciona como el método wait pero lee y evalúa los contenidos del fichero generado por el proceso hijo. Es posible que necesite atributos adicionales para guardar el nombre del fichero mediante el que se comunicarán padre e hijo.

Casiano Rodríguez León
2012-02-29