2

Is there a simple, reliable way to tell when an IPC::Run task has completed, i.e. any child process(es) have exited?

The docs are astonishingly silent on this.

It seems that looping on pumpable works, though it's not really documented clearly as the right way to do things:

use strict;
use warnings;
use IPC::Run;
use 5.12.0;

my $handle = IPC::Run::start(['sleep', '10']);

while ($handle->pumpable)
{
    sleep(0.5);
    # do other stuff in the event loop
    # so we don't want to block on finish
}

$handle->finish;

print("exited with '" . $handle->result . "'");

Is there a better option? finish blocks, but then you can't do other work in your event loop while you wait for the proc to finish.

I'm surprised there isn't a simple

$handle->running

or

$handle->finished

Am I missing something obvious?

Similarly, there doesn't seem to be a documented way to get the pid of the child(ren).

Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
  • The `$handle->finish` blocks, like `wait`. If you want to check before waiting, calling `result` _before_ child completed apparently throws an exception, so you can use that, by checking for the exception -- `eval { $res = $handle->result } or print "Still running\n";`. It's a kludge (should check for `$@` instead of `or`-it), but will that do? There's a number of other versions of `result`. There's also a `signal`, and I tried to send a `0` but am getting back nothing. There seems to be a hint in docs that pid's can't be had directly. – zdim Nov 17 '16 at 07:36
  • Ugh. Yeah, could try calling `result` and trapping the exception. Ugly :( . I should've been explicit that I'm looking for a non-blocking way, otherwise I'd just use `IPC::Run::run`. Thanks. – Craig Ringer Nov 17 '16 at 07:44
  • Right, I did get that, but mentioned blocking just in case. Ugly it is but I can't see a cleaner way right now. (I haven't used this module much. I'll look more, as I always wanted to check it out better.) – zdim Nov 17 '16 at 07:48
  • 1
    It's incredibly useful, highly recommended. This is about my only issue with it, along with its apparent decision to obscure the child proc's pid. – Craig Ringer Nov 17 '16 at 07:55
  • So, beating encapsulation isn't a good way, but: when you do `Dumper($handle)` you'll see the object data. (Need `use Data::Dumper;` for it.) From that, we can get the pid as `$handle->{KIDS}[0]{PID}`. That'd be for the first kid. This and the exception trick with `result` can be wrapped in subs, to lessen the pain a little. – zdim Nov 17 '16 at 08:00
  • Yeah, found that from the sources but didn't want to mention it here because really, it's breaking deep into the guts of the module. – Craig Ringer Nov 17 '16 at 08:03
  • Sure, that is a wrong way. I'll look, but hopefully some of people who know the module will come along. Let me know if you want to clean up (at least some) comments. – zdim Nov 17 '16 at 08:06

2 Answers2

1

Here is a workaround to get the child PID. Then you can wait for the child or set up a SIGCHLD handler. For example:

use feature qw(say);
use strict;
use warnings;

use IPC::Run qw( start );
use Proc::ProcessTable;

my $h = start ['sleep', '5'];

my $pt = Proc::ProcessTable->new();
my $kid;
for my $p (@{ $pt->table } ){
    if ( $p->ppid == $$ ) {
        if ( $p->cmndline =~ m{/bin/sleep} ) {
            $kid = $p->pid;
        }
    }
}

if ( defined $kid ) {
    say "Waiting for child with PID '$kid' to finish..";
    waitpid $kid, 0;
    say "Done.";
}
else {
    die "Could not find PID of child process\n";
}
Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174
1

I have not been able to find a clean way to do either, only other ways.

Information about whether children are running can be obtained by result related methods, and by pumpable (what is already used in the question).

The result method "Throws an exception if an out-of-range child number is passed". This happens if we query when no kids exited yet. So, with a single child process

sub only_kid_finished { eval { $_[0]->result }; return $@ ? 0 : 1 }

Better yet, results method returns a list of child exit values while it also "Throws an exception if the harness is not in a finished state". So it can be used the same way but with more kids it can also be used to keep track of how many exited/are left.

As used in the question, pumpable can also inform whether any processes are running

sub any_kids_left { return $_[0]->pumpable }

It relates to I/O channels or processes, but if anything is running it must return true, otherwise false.

The question of children PID is a bit vexing for me, since it is information that one may need to know. While it is right in the object, $handle->{KIDS}[0]{PID} (for first kid), I don't see how to retrieve it in a reasonable way. I'd think that a structure this basic can't change, so it would tempt me to actually use it, with checks. (Then I'd also be content with whatever I may get for poking at a class's internals, having deserved it.)

Once PID is retrieved one can check up on the process with kill 0, $pid. Note that this returns true (1) until the process has been reaped (even when it exited but is a zombie).

zdim
  • 64,580
  • 5
  • 52
  • 81