Commit 833d0937 authored by Leigh B Stoller's avatar Leigh B Stoller

Fix gaping race condition in ParRun() that was causing an infinite loop

when getting a termination signal. Also add an option to not redefine
the HUP handler, which is needed for the portal_monitor, which uses the
HUP signal to reopen the logfile (from syslogd).
parent 6f628c59
......@@ -44,6 +44,7 @@ use Carp;
use Date::Parse;
use Time::Local;
use Data::Dumper;
use POSIX qw(:signal_h);
# Configure variables.
my $TB = "@prefix@";
......@@ -359,6 +360,7 @@ sub ParRun($$$@)
my @results = ();
my $counter = 0;
my $signaled = 0;
my $nosighup = 0;
# We need this below.
require event;
......@@ -371,6 +373,8 @@ sub ParRun($$$@)
if (exists($options->{'maxchildren'}));
$maxwaittime = $options->{'maxwaittime'}
if (exists($options->{'maxwaittime'}));
$nosighup = $options->{'nosighup'}
if (exists($options->{'nosighup'}));
}
#
......@@ -379,20 +383,21 @@ sub ParRun($$$@)
my $coderef = sub {
my ($signame) = @_;
print STDERR "Caught SIG${signame}! Killing parrun ...";
print STDERR "Caught SIG${signame} in $$! Killing parrun ...\n";
$SIG{TERM} = 'IGNORE';
$signaled = 1;
foreach my $pid (keys(%children)) {
kill('TERM', $pid);
print STDERR "Sending HUP signal to $pid ...\n";
kill('HUP', $pid);
}
sleep(1);
};
local $SIG{QUIT} = $coderef;
local $SIG{TERM} = $coderef;
local $SIG{HUP} = $coderef;
local $SIG{INT} = $coderef;
local $SIG{HUP} = $coderef if (!$nosighup);
#
# Initialize return.
......@@ -401,40 +406,59 @@ sub ParRun($$$@)
$results[$i] = -1;
}
while (@objects || keys(%children)) {
while ((@objects && !$signaled) || keys(%children)) {
#
# Something to do and still have free slots.
#
if (@objects && keys(%children) < $maxchildren && !$signaled) {
# Space out the invocation of child processes a little.
sleep(1);
#
# Run command in a child process, protected by an alarm to
# ensure that whatever happens is not hung up forever in
# some funky state.
#
my $object = shift(@objects);
my $syspid = fork();
if ($syspid) {
my $newsigset = POSIX::SigSet->new(SIGQUIT,SIGINT,SIGTERM,SIGHUP);
my $oldsigset = POSIX::SigSet->new;
if (! defined(sigprocmask(SIG_BLOCK, $newsigset, $oldsigset))) {
print STDERR "sigprocmask (BLOCK) failed!\n";
return -1;
}
if (!$signaled) {
#
# Just keep track of it, we'll wait for it finish down below
# Run command in a child process, protected by an alarm to
# ensure that whatever happens is not hung up forever in
# some funky state.
#
$children{$syspid} = [$object, $counter, time()];
$counter++;
}
else {
$SIG{TERM} = 'DEFAULT';
$SIG{QUIT} = 'DEFAULT';
$SIG{HUP} = 'DEFAULT';
# So randomness is not the same in different children
srand();
my $object = shift(@objects);
my $syspid = fork();
if ($syspid) {
#
# Just keep track of it, we'll wait for it finish down below
#
$children{$syspid} = [$object, $counter, time()];
$counter++;
}
else {
$SIG{TERM} = 'DEFAULT';
$SIG{QUIT} = 'DEFAULT';
$SIG{HUP} = 'DEFAULT';
$SIG{INT} = 'IGNORE';
# Unblock in child after resetting the handlers.
if (! defined(sigprocmask(SIG_SETMASK, $oldsigset))) {
print STDERR "sigprocmask (UNBLOCK) failed!\n";
}
# So randomness is not the same in different children
srand();
# So we get the event system fork too ...
event::EventFork();
exit(&$function($object));
# So we get the event system fork too ...
event::EventFork();
exit(&$function($object));
}
}
# Unblock after critical section.
if (! defined(sigprocmask(SIG_SETMASK, $oldsigset))) {
print STDERR "sigprocmask (UNBLOCK) failed!\n";
return -1;
}
}
elsif ($signaled) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment