I'm trying to decide wether it's safe to access a common (read: shared between handler-code and rest of the programm) data structure from a signal handler in perl (v5.14.2) built for x86_64-linux-thread-multi
, but target platform is solaris11).
perlipc has the following sample code:
use POSIX ":sys_wait_h"; # for nonblocking read
my %children;
$SIG{CHLD} = sub {
# don't change $! and $? outside handler
local ($!, $?);
my $pid = waitpid(-1, WNOHANG);
return if $pid == -1;
return unless defined $children{$pid};
delete $children{$pid};
cleanup_child($pid, $?);
};
while (1) {
my $pid = fork();
die "cannot fork" unless defined $pid;
if ($pid == 0) {
# ...
exit 0;
} else {
$children{$pid}=1;
# ...
system($command);
# ...
}
}
So, %children
is accessed from the while-loop and the handler. This seems to be no problem as:
- There won't be two processes having the same
pid
- Access is keyed by
pid
(I am not sure if$childer{pid}=1
is atomic and interruptible without causing corruption, though.)
Now, i'm trying to do even more in my handler:
my %categoryForPid;
my %childrenPerCategory;
$SIG{CHLD} = sub {
# ... acquire pid like above
my $category = $categoryForPid{$pid};
$childrenPerCategory{$category}--;
delete $categoryForPid{$pid};
}
while (1) {
# ... same as above
} else {
$children{$pid}=1;
my $category = # ... chose some how
$childrenPerCategory{$category}++;
$categoryForPid{$pid} = $category;
# ...
}
}
The idea here is: every child belongs to a certain category (N to 1). I want to keep track of how many children per category exist. That information could be derived from $categoryForPid
, but i think that might be problematic also (e.g., when the subroutine doing the computation gets interrupted while summing up).
So my question is:
- Do I need to synchronize here somehow?
And on a side note:
- Are nested invocations of the signal handler possible in perl 5.12, or are they linearized by the interpreter?
Update
In addition to the problem spotted by @goldilocks and his proposed solution I block signals now while updating the data structures to ensure "atomicity":
my $sigset = POSIX::SigSet->new(SIGCHLD);
sub ublk {
unless (defined sigprocmask(SIG_UNBLOCK, $sigset)) {
die "Could not unblock SIGCHLD\n";
}
}
sub blk {
unless (defined sigprocmask(SIG_BLOCK, $sigset)) {
die "Could not block SIGCHLD\n";
}
}
while (1) {
# ... same as above
} else {
blk;
$children{$pid}=1;
my $category = # ... chose some how
$childrenPerCategory{$category}++;
$categoryForPid{$pid} = $category;
ublk;
# ...
}
}