Håkon Hægland
Håkon Hægland

Reputation: 40748

Wait for child processes with timeout

I would like to wait for two processes (or more) to finish, or a timer to count down. The timer countdown will be output in the terminal window. For example:

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

my $pid1 = fork();
if ( $pid1 == 0 ) {
    runTask1();
    exit 0;
}

my $pid2 = fork();
if ( $pid2 == 0 ) {
    runTask2();
    exit 0;
}

my $timer = startTimer();

say "Waiting for child processes..";
my $counter = 20;
my $i       = 0;
while (1) {
    my $pid = wait;
    last if ( $pid == -1 );
    if ( $pid == $timer ) {
        $counter--;
        say $counter;
        $timer = startTimer();
    } else {
        say "Child with PID=$pid finished..";
        $i++;
        last if $i == 2;
    }
}

say "Done.";

sub startTimer {

    my $pidTimer = fork();
    if ( $pidTimer == 0 ) {
        sleep 1;
        exit 0;
    }
    return $pidTimer;
}

sub runTask1 {
    sleep 10;
    exit 0;
}

sub runTask2 {
    sleep 5;
    exit 0;
}

My concern about this approach is that I create a forked sub process for the timer, which seems like overkill. Is this necessary, or is there a simpler way?

Upvotes: 2

Views: 2135

Answers (1)

Sobrique
Sobrique

Reputation: 53478

Have a look at alarm() - which triggers a kill signal ALRM after the specified timeout. Which'll either just kill the process, or you can use:

    $SIG{'ALRM'} = \&some_sub_to_handle_alarms;

alarm() doesn't propagate to forked processes, so you can set it on your 'parent' so it just times out waiting for children.

You probably don't need to fork your pidTimer though - you can just have your main process sit in a loop.

The following demonstrates:

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

my $pid1 = fork();
if ( $pid1 == 0 ) {    # Simulated Task 1
    sleep 10;
    exit 0;
}

my $pid2 = fork();
if ( $pid2 == 0 ) {    # Simulated Task 2
    sleep 5;
    exit 0;
}

say "Waiting for child processes..";
my $counter = 20;
local $SIG{ALRM} = sub {
    say --$counter;
    alarm 1;
};
alarm 1;

while ((my $pid = wait) != -1) {
    say "Child with PID=$pid finished..";
}

alarm 0;
say "Done.";

Outputs:

Waiting for child processes..
19
18
17
16
Child with PID=55240 finished..
15
14
13
12
11
Child with PID=55239 finished..
Done.

Upvotes: 5

Related Questions