kennethk has asked for the wisdom of the Perl Monks concerning the following question:
Yesterday, I had a rude awakening when I managed to take down the server my test suite was running on. Analysis after a hard reboot of the server showed that in exceptional cases, the utility seems to start grabbing memory in an infinite loop - it consumes the 64GB of physical memory in a few minutes. So today's job was modifying the test so it would abort a given test after a suitable timeout.
Looking through the archives, I first tried following Re: killing a program called with system() if it takes too long? by implementing an alarm (in a single threaded version), which resulted in the creation of zombies that continued chewing through my memory. In order to actually kill the computations, I combined a pipe with repeated calls to `ps --ppid` and kill to get all spawned processes (inspired by almut's Re^2: Killing children's of children). I then discovered that alarms don't work in a threaded context, so I followed ikegami's advice in Re^3: Threads and ALARM() - There is a solution or workaround? and used select on the pipe instead of signals, resulting in some code that seems to be working (special thanks to duff's Using select and IO::Select tutorial). So I replaced
my $content = `$command 2>&1`;
with
my $pid = open my $calc, '-|', "$command 2>&1" or die "Pipe failed on open: $!\n"; my $vector = ''; vec($vector,fileno($calc),1) = 1; # Flip bit for pipe unless (select($vector,undef,undef,$timeout)) { # Calculation +is hanging # collect list of spawned processes my @pids = $pid; my $i = -1; push @pids, `ps --ppid $pids[$i] -o pid` =~ /\d+/g while + ++$i < @pids; kill 9, $_ for @pids; die "Calculation call failed to return with $timeout secon +ds\n"; } local $/; # Slurp my $content = <$calc>; close $calc;
where both are wrapped in eval blocks. So my questions are:
|
|---|