Perl sleep terminated by SIGCHLD - perl

#!/usr/bin/perl
use POSIX ":sys_wait_h";
$SIG{CHLD} = \&REAPER;
sub REAPER {
my $pid;
while (($pid = waitpid(-1, WNOHANG)) > 0) {
print "where is here,$pid\n";
}
}
sub child {
print "I'm child, pid=$$.\n";
sleep 2;
}
$lid = fork();
if ($lid == 0) {
&child;
exit;
} else {
sleep 1000;
print "I am parent, child pid : $lid\n";
}
Output:
I'm child, pid=11839.
where is here,11839
I am parent, child pid : 11839
The above is my Perl script. The output is right, but one strange thing is that it prints I am parent, child pid : 11839 immediately after the last output. Why didn't the sleep 1000 have any effect?

This is documented: "May be interrupted if the process receives a signal". The whole point is to allow signal handlers to run. Just go back to sleep if it's not time to wake up.
use Time::HiRes qw( time sleep ); # Optional.
sub unint_sleep($) {
my $sleep_til = time + $_[0];
for (;;) {
my $sleep_dur = time - $sleep_til;
last if $sleep_dur <= 0;
sleep($sleep_dur);
}
}

Please always use strict and use warnings, and declare your symbols using my at their point of definition. This applies especially when you are asking for help, as these measures can reveal simple bugs that are otherwise easily overlooked
sleep is implemented by using setitimer to request a SIGALRM after a specified interval, and then pause to suspend the process until it gets the signal.
But if a SIGCHLD comes first this will also wake the process.
The signal mask cannot be set to prevent this as otherwise the SIGCHLD wouldn't get serviced

Related

Reading from a file descriptor in a non-blocking way with Perl

Let's say I have this:
pipe(READ,WRITE);
$pid = fork();
if ($pid == 0) {
close(READ);
# do something that may be blocking
print WRITE "done";
close(WRITE);
exit(0);
} else {
close(WRITE);
$resp = <READ>;
close(READ);
# do other stuff
}
In this situation, it's possible for the child to hang indefinitely. Is there a way I can read from READ for a certain amount of time (ie, a timeout) and if I don't get anything, I proceed in the parent with the assumption that the child is hanging?
Typically, in C or Perl, you use select() to test if there is any input available. You can specify a timeout of 0 if you like, though used 1 second in the example below.:
use IO::Select;
pipe(READ,WRITE);
$s = IO::Select->new();
$s->add(\*READ);
$pid = fork();
if ($pid == 0) {
close(READ);
# do something that may be blocking
for $i (0..2) {
print "child - $i\n";
sleep 1;
}
print WRITE "donechild";
close(WRITE);
print "child - end\n";
exit(0);
} else {
print "parent - $pid\n";
close(WRITE);
for $i (0..10) {
print "parent - $i\n";
# 1 second wait (timeout) here. Can be 0.
print "parent - ", (#r=$s->can_read(1))?"yes":"no", "\n";
last if #r;
}
$resp = <READ>;
print "parent - read: $resp\n";
close(READ);
# do other stuff
}
Is there a way I can read from READ for a certain amount of time (ie, a timeout) and if I don't get anything, I proceed in the parent with the assumption that the child is hanging?
When you fork, you are working with two entirely separate processes. You're running two separate copies of your program. Your code cannot switch back and forth between the parent and child in your program. You're program is either the parent or the child.
You can use alarm in the parent to send a SIGALRM to your parent process. If I remember correctly, you set your $SIG{ALRM} subroutine, start your alarm, do your read, and then set alarm back to zero to shut it off. The whole thing needs to be wrapped in an eval.
I did this once a long time ago. For some reason, I remember that the standard system read didn't work. You have to use sysread. See Perl Signal Processing for more help.

Get the value of a process executed in a child back to parent

I'm looking for a solution which allows me to return the values of a process executed in a child back to the parent process. Currently i try this but have no idea where to hook the return value:
use Proc::ProcessTable;
use POSIX qw(:signal_h :errno_h :sys_wait_h);
$SIG{CHLD} = \&REAPER;
for my $count (1..10) { # start a few demo childs
if (fork () == 0) {
&startChild;
exit 0;
}
}
do {
print "Working\n";
sleep 1;
} while (chkChildProcess());
sub startChild {
print "Starting Child $$\n";
system("date"); #==>Need to get the output of "date" back to parent
sleep 2 + rand 7;
print "End Child $$\n";
}
sub chkChildProcess {
for my $p (#{new Proc::ProcessTable->table}){
if ($p->ppid == $$){
$curPID{$$}=$p->pid;
return 1;
}
}
return undef;
}
sub REAPER {
my $pid;
$pid = waitpid(-1, &WNOHANG);
if ($pid == -1) {
# no child waiting. Ignore it.
} elsif (WIFEXITED($?)) {
print "Process $pid exited.\n";
} else {
print "False alarm on $pid.\n";
}
$SIG{CHLD} = \&REAPER; # in case of unreliable signals
}
Any help would be great.
The bg_eval and bg_qx methods of Forks::Super were made to solve this problem.
use Forks::Super 'bg_eval';
my #result;
for my $count (1 .. 10) {
$result[$count] = bg_eval {
my $date = `date`;
sleep 2 + rand 7;
return $date;
};
}
print "$result[$_]\n" for 1..10;
The block after bg_eval is run asynchronously in a background process. When the background process is finished, the variable $result[$count] will be populated with the result.
When you print $result[$_], one of two things will happen. If the background process associated with that variable is finished, it will contain its return value. If the background process is not finished, it will wait for the process to finish, and then make the return value available in that value.
It looks like you may want to use Parallel::ForkManager, returning the value from the child via the data_structure_reference parameter of the finish method to a run_on_finish callback in the parent.
To capture the output, the easiest way is to use IPC::System::Simple's capture or capturex.
you could use threads::shared instead of fork, create a shared variable, lock it and write into it. keep in mind that locking is reeeally slow!
See also this post on perlmonks on why locking the variable is necessary.

What else can i do 'sleep' when the sleep() can't work well with alarm?

There are many documents say "you should avoid using sleep with alarm, since many systems use alarm for the sleep implementation". And actually, I'm suffering with this problem.
So does anyone can help me that what else i can do 'sleep' when the sleep() can't work well with alarm? I have already tried 'usleep' of the Time::HiRes module, and select() function. But they didn't work either.
Seeing as you're being interrupted by alarms, and so can't reliably use sleep() or select(), I suggest using Time::HiRes::gettimeofday in combination with select().
Here's some code that I've not tested. It should resist being interrupted by signals, and will sleep for the desired number of seconds plus up to 0.1 seconds. If you're willing to burn more CPU cycles doing nothing productive, you can make the resolution much better:
...
alarm_resistant_sleep(5); # sleep for 5 seconds, no matter what
...
use Time::HiRes;
sub alarm_resistant_sleep {
my $end = Time::HiRes::time() + shift();
for (;;) {
my $delta = $end - Time::HiRes::time();
last if $delta <= 0;
select(undef, undef, undef, $delta);
}
}
You can try AnyEvent:
use AnyEvent;
my $cv = AnyEvent->condvar;
my $wait_one_and_a_half_seconds = AnyEvent->timer(
after => 1.5,
cb => sub { $cv->send }
);
# now wait till our time has come
$cv->recv;
You can sleep on a new process via system:
system ( "sleep", 5 );
Or did I misunderstand the question?
When using (from MySQL forum)
use Sys::SigAction qw( set_sig_handler );
eval {
my $hsig = set_sig_handler( 'ALRM', sub { my $canceled = 1; die; }, { mask=>[ qw( INT ALRM ) ] ,safe => 0 } );
alarm($timeout);
...
alarm(0);
}
I noticed that any subsequent calls made to sleep($delay) with $timeout shorter than $delay would end up with the script execution being terminated, and the print of "Alarm clock".
The workaround I've found is to call alarm() again but with an improbably large value (3600), and cancel that alarm right after.
eval {
alarm(3600);
print " .... Meeeep ...."; # Some trace
alarm(0);
};
Then I can use sleep() with no interference anymore.
Example below (live code snippet):
sub unmesswithsleep {
eval {
alarm(3600);
&tracing (8, " .... Meeeep ....");
alarm(0);
};
}
sub lockDBTables {
return (0) unless ($isdbMySQLconnect);
my $stm = qq {
LOCK TABLES
myBIGtable WRITE
};
my $timeout = 60; # This is the timer set to protect against deadlocks. Bail out then.
eval {
my $h = set_sig_handler( 'ALRM', sub { my $canceled = 1; die; }, { mask=>[ qw( INT ALRM ) ] ,safe => 0 } );
alarm($timeout);
my $res = $dbmyh->do($stm) + 0;
alarm(0); # Reset alarm
};
if ( $# =~ m/Die/i ) {
$isdbTabledlocked = 0;
&tracerr (0, "FATAL: Lock on Tables has NOT been acquired within ${timeout}s. Lock is set to <$isdbTabledlocked>.");
&unmesswithsleep(); # MUST be called each time alarm() is used
return (0);
} else {
$isdbTabledlocked = 1;
&tracing (2, " Good: Lock on Tables has been acquired in time. Lock is set to <$isdbTabledlocked>.");
&unmesswithsleep(); # MUST be called each time alarm() is used
return (1);
}
# Can use sleep() now.
}
try
print "Start\n";
select undef, undef, undef, 1;
print "End\n";
This will sleep for 1 second.
It sounds like your code that sleeps is being interrupted by some code that sets an alarm. This is by design so you're seeing the expected behavior. In other words an alarm *should always interrupt a sleep call.
If you're looking for a pure perl way to sleep without being interrupted by an alarm you can do this by installing your own alarm signal handler. This way when your code gets an alarm it won't interrupt your processing.
However, an important caveat is that this will delay any alarm that was set by other code. The other code will receive the alarm late; after your code completes. This means that if you want to play well with others you're better off using one of the other solutions.
Here is an example:
#!/usr/bin/perl
use POSIX;
use strict;
use warnings;
# set an alarm
print "Setting alarm\n";
alarm 1;
my $old_alarm;
my $snoozed;
{
# store the previous alarm handler (if any)
$old_alarm = $SIG{ALRM};
# override the alarm handler so that we don't
# get interrupted
local $SIG{ALRM} = sub {
print "got alarm; snoozing\n";
# record the fact that we caught an alarm so that
# we can propagate it when we're done
$snoozed++;
};
# sleep for a while.
for (1 .. 3) {
print "z" x $_ ,"\n";
sleep 1;
}
}
# replace the old sleep handler;
$SIG{ALRM} = $old_alarm
if $old_alarm;
# if we had to snooze fire an immediate alarm;
if ($snoozed) {
POSIX::raise(POSIX::SIGALRM);
}
The documentation you reference hints at but does not describe a different symptom. The main thing you need to worry about when sleep is implemented via alarm is having your alarm reset when someone calls sleep.
*Apparently there are some versions of perl (e.g.: old Win32) where an alarm doesn't interrupt sleep.

How can I limit the time spent in a specific section of a Perl script?

Is there any way to build some time counter that enable parts of a script to run as long it ticks? For example, I have the following code:
for my $i (0 .. $QUOTA-1) {
build_dyna_file($i);
comp_simu_exe;
bin2txt2errormap($i);
}
Theoretically I want to run this loop for 3 minutes, even if the loop instructions haven't finished yet, it should still break out of the loop after exactly 3 minutes.
Actually the program opens a time counter window that works in parallel to part of the script (each time I call it).
Additionally, the sub call 'comp_simu_exe' run an outside simulator (in the shell) that when time out ends - this process must also killed (not suppose to return after a while).
sub comp_simu_exe{
system("simulator --shell");
}
Is there any connection between the dead coming problem to the system function call ?
You can set an alarm that will break out of your code after a specified amount of seconds:
eval {
local $SIG{ ALRM } = sub { die "TIMEOUT" };
alarm 3 * 60;
for (my $i = 0 ; $i <$QUOTA ; $i++) {
build_dyna_file($i);
comp_simu_exe;
bin2txt2errormap($i);
}
alarm 0;
};
if ( $# && $# =~ m/TIMEOUT/ ) {
warn "operation timed out";
}
else {
# somebody else died
alarm 0;
die $#;
}
Or, if you really need the loop to run at least three times, no matter how long this might take:
eval {
my $t0 = time;
local $SIG{ ALRM } = sub { die "TIMEOUT" };
for (my $i = 0 ; $i <$QUOTA ; $i++) {
build_dyna_file($i);
comp_simu_exe;
bin2txt2errormap($i);
if ( $i == 3 ) {
my $time_remaining = 3 * 60 - time - $t0;
alarm $time_remaining if $time_remaining > 0;
}
}
alarm 0;
};
Here's a second answer that deals with the case of timing-out a second process. Use this case to start your external program and make sure that it doesn't take too long:
my $timeout = 180;
my $pid = fork;
if ( defined $pid ) {
if ( $pid ) {
# this is the parent process
local $SIG{ALRM} = sub { die "TIMEOUT" };
alarm 180;
# wait until child returns or timeout occurs
eval {
waitpid( $pid, 0 );
};
alarm 0;
if ( $# && $# =~ m/TIMEOUT/ ) {
# timeout, kill the child process
kill 9, $pid;
}
}
else {
# this is the child process
# this call will never return. Note the use of exec instead of system
exec "simulator --shell";
}
}
else {
die "Could not fork.";
}
The way you deal with timeouts like this in Perl is the alarm function. It will send the signal ALRM to your process after the number of seconds you pass into it. Be careful if the code you are trying to setup the timeout for calls sleep, as they do not mix well on many platforms. The basic structure looks like this:
#start a block eval to stop the die below from ending the program
eval {
#set the signal handler for the ALRM signal to die if it is run
#note, the local makes this signal handler local to this block
#eval only
local $SIG{ALRM} = sub { die "timeout\n" };
alarm $wait; #wait $wait seconds and then send the ALRM signal
#thing that could take a long time
alarm 0; #turn off the alarm (don't send the signal)
#any true value will do, if this line is reached the or do below won't run
1;
} or do {
#if we are in this block something bad happened, so we need to find out
#what it was by looking at the $# variable which holds the reason the
#the code above died
if ($# eq "timeout\n") {
#if $# is the timeout message then we timed out, take the
#proper clean up steps here
} else {
#we died for some other reason, possibly a syntax error or the code
#issued its own die. We should look at $# more carefully and determine
#the right course of action. For the purposes of this example I will
#assume that a message of "resource not available\n" is being sent by
#the thing that takes a long time and it is safe to continue the program.
#Any other message is unexpected.
#since we didn't timeout, but the code died, alarm 0 was never called
#so we need to call it now to prevent the default ALRM signal handler
#from running when the timeout is up
alarm 0;
if ($# eq "resource not available\n") {
warn $#;
} else {
die $#;
}
}
or written more compactly:
eval {
local $SIG{ALRM} = sub { die "timeout\n" };
alarm $wait; #wait $wait seconds and then send the ALRM signal
#thing that could take a long time
alarm 0;
1;
} or do {
die $# unless $# eq "timeout\n" or $# eq "resource not available\n";
alarm 0;
warn $#;
}

How do I make a CGI::Fast based application kill -HUP aware?

I have application that works using Perl's CGI::Fast.
Basically mainloop of the code is:
while (my $cgi = CGI::Fast->new() ) {
do_whatever( $cgi );
}
Now, I wanted to add ability to kill it, but without disrupting currently processed request. To do so, I added handling to SIGHUP. More or less along the lines:
my $sighupped = 0;
$SIG{ 'HUP' } = sub { $sighupped = 1; };
while (my $cgi = CGI::Fast->new() ) {
do_whatever( $cgi );
exit if $sighupped;
}
Which works really great when it comes to "not disturbing process while it handles user request". But, if the process is waiting for new request the sighup-based exit will not be executed until it will finally get some request and process it.
It there any workaround for this? What I would like to achieve it to make the script exit immediately if the HUP (or other signal, it can be changed) reaches it while waiting for request.
You could use the block version of the eval function and the alarm function to add a timeout to Fast::CGI. In the code below I have set a five second timeout. If it times out it will go back to the start of the loop which will check to see if $continue has been set to zero yet. If it hasn't then it we start a new CGI::Fast object. This means that a maximum of five seconds will go by after you send a HUP before the code will start to stop if it was waiting for a new CGI::Fast object (the timeout has no affect on the rest of the loop).
my $continue = 1;
$SIG{HUP} = sub { $continue = 0 };
while ($continue) {
my $cgi;
eval {
local $SIG{ALRM} = sub { die "timeout\n" };
alarm 5; #set an alarm for five seconds
$cgi = CGI::Fast->new;
alarm 0; #turn alarm off
};
if ($#) {
next if $# eq "timeout\n"; #we will see the HUP signal's change now
#died for a reason other than a timeout, so raise the error
die $#;
}
last unless defined $cgi; #CGI::Fast has asked us to exit
do_stuff($cgi);
}
#clean up
You could redeclare your SIGnals based on where the handler is.
If you're outside fast-cgi while loop or inside the fast-cgi polling and thus not in
the event loop terminate (basically the default). If you're inside the eventloop logic wait for logic to end and terminate.
Pardon my lack of an editor at the moment.
$inlogic=0;
$hupreq=0;
SIG{SIGHUP}={ exit unless($inlogic); $hupreq=1;}
while()
{
$inlogic=1;
function();
$inlogic=0;
exit if($hupreq);
}