shrubbery has asked for the wisdom of the Perl Monks concerning the following question:

Hello all,

I built a class that's main purpose is to execute various commands via backticks. I then check $? to see what happens afterwards and return it back through the method.

Anyway, I have a very strange problem. One command seems to time out all the time. But after EXACTLY 56 timeouts of this certain command, it refuses to run anymore and returns $? as -1 and blank in stdout and stderr.

The code is almost straight from the Perl Cookbook.

$SIG{ALRM} =\&time_out; eval { alarm $time_out; @args = `$command 2>&1`; alarm (0); }; if ($@) { $err = 100; } # error codes from wait are bit shifted # if its 100, we timed out $err = ($? >> 8) if ($err != 100); sub time_out{ # Error: Timeout on process alarm 0; die; } return ({err => $err, data => \@args});

Any ideas?

Edit kudra, 2001-11-07 Added code tags

Replies are listed 'Best First'.
Re: timeout process problems
by bluto (Curate) on Nov 07, 2001 at 05:58 UTC
    In general, the 'eval/alarm' solution can not be used when you run backticks or system since the child doesn't always get the hint and die. It works ok on internal perl commands. See my first PM post here.

    I'm not sure why you are getting 56 timeouts. Just for kicks, try doing a 'ps' during this and see if you are accumulating child processes (and eventually reach your limit).

    Since then I've tried other solutions, but can't say I've ever come across what I thought was especially clean. I don't like using signals much since they are unsafe and I'm paranoid (as my previous post details). I tend to fork/exec the process off, send the output to a temp file, and then (lamely) do a nonblocking waitpid call in a polling loop where I sleep 1 second each time through to check on it. You can replace the waitpid loop with something (untested) like ...

    eval { local $SIG{ALRM} = sub { die 'timedout' }; alarm $time_to_wait; waitpid($pid, 0); $exit_code = $?; alarm(0); } if ($@) { $exit_code = ($@ =~ /timedout/)? 100 : 200; }
    ... but why bother? (The polling solution does keep your process in memory during the wait, and uses a small amount of CPU time.) FWIW, you can also do the same kind of thing without a temp file by redirecting output to a pipe to the parent (if you are careful).

    bluto

      Just as a FYI, I was just poking around my code and realized they child processes were NOT dying with a certain command and becoming zombies. Sure enough there were 56 zombie processes. On the 57th timeout, no child process or zombie would spawn so I said to myself, "maybe there's a limit on zombies somewhere?" So I set $SIG{CHLD} to 'IGNORE' to prevent zombies. It went OK for awhile but then the same problem appeared again even with no zombies!

      Am still looking to see whats truly causing the command to not work. I'll give your method a shot in a bit.

        Ahh, I found out that $! is returning "Too many files open" during the 57th try. Any ideas?