waitpid and alarms

S

Steve East

Below is a Mickey Mouse script to illustrate a problem I'm seeing with
waitpid and alarms in perl 5.8.0. The manifestation of the problem is that
waitpid returns an unexpected process id. Unexpected because it's not the
one I'm waiting on. It seems that if an alarm goes off during waitpid then
it will return whatever process happens to be available for reaping rather
than the one it's specifically waiting for. Here's the code:

my @replpid;
for (my $i = 0; $i <= 7; $i++) {
if ($replpid[$i] = fork) {
print "Forking pid $replpid[$i]\n";
} elsif (defined $replpid[$i]) {
my $secs = int rand 10;
exec "sleep $secs";
} else {
die "Unable to fork sleep: $!\n";
}
}

$SIG{ALRM} = \&alarmist;
alarm 5;

foreach my $pid (@replpid) {
print "pid = $pid, waitpid = ", waitpid($pid, 0), "\n";
}

sub alarmist {
print "Alarm signal\n";
return;
}

And here's the output:

Forking pid 20097
Forking pid 20099
Forking pid 20101
Forking pid 20103
Forking pid 20105
Forking pid 20107
Forking pid 20109
Forking pid 20111
pid = 20097, waitpid = 20097
Alarm signal
pid = 20099, waitpid = 20109
pid = 20101, waitpid = 20101
pid = 20103, waitpid = 20103
pid = 20105, waitpid = 20105
pid = 20107, waitpid = 20107
pid = 20109, waitpid = -1
pid = 20111, waitpid = 20111

Note how after the alarm goes off, the wait on pid 20099 returns pid 20109.
Naturally, the wait on 20109 then returns -1. This wasn't happening with
perl 5.005.02.

Thanks,
Steve.
 
S

Steve East

Purl Gurl said:
Steve East wrote:

(snipped)



sleep ($secs);

The point of the exercise was actually not to do a sleep, it was simply to
have a command that ran for a number of seconds. The actual command takes
too much setup code for a sample script. And I want it behind an exec
because that's how the real script runs. Plus changing it to an inline sleep
causes the child processes to execute the rest of the script.
$SIG{ALRM} = \&alarmist;


Purl Gurl
--

These are my results using Perl 5.8.0 with
your original code, except iterations are
reduced to three ($i <= 3) to minimize
produced prints.


Forking pid -1856611
Forking pid -1857187
Forking pid -1856483
Forking pid -1862691
Bad command or file name
Bad command or file name
Bad command or file name
Bad command or file name
pid = -1856611, waitpid = -1856611
pid = -1857187, waitpid = -1857187
pid = -1856483, waitpid = -1856483
pid = -1862691, waitpid = -1862691

Guess you're on a Win32 system, with no sleep command. I'm on Solaris.
This code produces results which are
quite different:

#!perl

$SIG{ALRM} = \&alarmist;
alarm 5;

my @replpid;
for (my $i = 0; $i <= 3; $i++)
{
if ($replpid[$i] = fork)
{ print "Forking pid $replpid[$i]\n"; }
elsif (defined $replpid[$i])
{
my $secs = int rand 10;
sleep ($secs);
}
else
{ die "Unable to fork sleep: $!\n"; }
}

foreach my $pid (@replpid)
{ print "pid = $pid, waitpid = ", waitpid($pid, 0), "\n"; }

sub alarmist
{
print "Alarm signal\n";
return;
$SIG{ALRM} = \&alarmist;
}

Unfortunately, this doesn't match what I need.
PRINTED RESULTS:
________________

Forking pid -1856523
Forking pid -1855819
Forking pid -1856095
pid = -1856523, waitpid = -1
pid = -1855819, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
Forking pid -1862819
pid = -1856523, waitpid = -1
pid = -1855819, waitpid = -1
pid = 0, waitpid = -1
pid = -1862819, waitpid = -1862819
Forking pid -1862815
Forking pid -1985279
Forking pid -1984167
pid = 0, waitpid = -1
pid = -1862815, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = -1862815, waitpid = -1
pid = -1984167, waitpid = -1
pid = 0, waitpid = -1
Forking pid -1983479
pid = 0, waitpid = -1
pid = -1862815, waitpid = -1
pid = 0, waitpid = -1
pid = -1983479, waitpid = -1983479
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = -1985279, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
Forking pid -1983479
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = -1983479, waitpid = -1983479
Forking pid -1983627
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = -1985279, waitpid = -1985279
pid = -1983627, waitpid = -1983627
Forking pid -1989687
pid = 0, waitpid = -1
pid = -1862815, waitpid = -1862815
pid = -1984167, waitpid = -1984167
pid = -1989687, waitpid = -1989687
pid = -1856523, waitpid = -1
pid = -1855819, waitpid = -1
pid = -1856095, waitpid = -1
pid = 0, waitpid = -1
Forking pid -1862111
pid = -1856523, waitpid = -1856523
Alarm signal
pid = -1855819, waitpid = -1
pid = -1856095, waitpid = -1856095
pid = -1862111, waitpid = -1862111
Forking pid -1856523
pid = -1856523, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = -1856523, waitpid = -1
pid = 0, waitpid = -1
pid = -1856523, waitpid = -1
pid = 0, waitpid = -1
Forking pid -1856055
pid = -1856523, waitpid = -1
pid = 0, waitpid = -1
pid = 0, waitpid = -1
pid = -1856055, waitpid = -1856055
Forking pid -1862819
pid = -1856523, waitpid = -1856523
pid = 0, waitpid = -1
pid = -1856523, waitpid = -1
pid = -1862819, waitpid = -1862819

Looks like all the child processes executing the waitpid loop. Not good.

Steve.
 
S

Steve East

Purl Gurl said:
This code produces results which are
quite different:

#!perl

$SIG{ALRM} = \&alarmist;
alarm 5;

my @replpid;
for (my $i = 0; $i <= 3; $i++)
{
if ($replpid[$i] = fork)
{ print "Forking pid $replpid[$i]\n"; }
elsif (defined $replpid[$i])
{
my $secs = int rand 10;
sleep ($secs);
}
else
{ die "Unable to fork sleep: $!\n"; }
}

foreach my $pid (@replpid)
{ print "pid = $pid, waitpid = ", waitpid($pid, 0), "\n"; }

sub alarmist
{
print "Alarm signal\n";
return;
$SIG{ALRM} = \&alarmist;
}

Your version of the script (but with an exit after the sleep) run on Solaris
produces:

Forking pid 21977
Forking pid 21978
Forking pid 21979
Forking pid 21980
pid = 21977, waitpid = 21977
Alarm signal
pid = 21978, waitpid = 21979
pid = 21979, waitpid = -1
pid = 21980, waitpid = 21980

Same problem. Looks like Win32 works, but not Solaris or Linux or HP-UX...

Steve.
 
S

Steve East

Purl Gurl said:
Actually, the most important part does. You need
to reset your $SIG{ALRM} each time your alarmist
sub-routine is called. Once called, using your
original code, $SIG{ALRM} is no longer set to
your sub-routine reference; it becomes null.

Check me on this, perhaps Solaris behaves differently.

Solaris must be different. I only need to set the alarm handler once. It is
then called for each instance of the alarm going off. Though in the sample
code that would only be once anyway.
(snipped results)


Based upon my understanding of what you are trying to do, results
are in keeping with expectations. I read polling and at least one
alarm event triggered, all in an orderly predictable fashion.

But only on Win32...
I wish you luck with your project.

Thanks,
Steve.
 
S

Steve East

Purl Gurl said:
You might want to write a test jig to verify this. Usually
interrupt signals reset to default after usage. They do not
behave like a globally set environment variable.

I cannot speak with authority, though, about this Solaris
specific function.

Been running the code daily for over a year on 5.005.02 and the alarms have
worked repeatedly and flawlessly. But your point is well taken, especially
as I intend to start running on Win32 in the not too distant future. Here's
a quick test:

$SIG{ALRM} = \&alarmist;

alarm 2;
sleep 5;
alarm 2;
sleep 5;
alarm 2;
sleep 5;

sub alarmist
{
print "Alarm signal\n";
return;
}

which produces:

Alarm signal
Alarm signal
Alarm signal

At least it does on Solaris :) Still works if I use "select undef, undef,
undef, 5" instead of sleep. Which I prefer.

Thanks,
Steve.
 
S

Steve East

Purl Gurl said:
HP-UX...


I have to laugh. That is really funny. This seems quite the reversal
of common events. Usually Win32 does not handle Unix type coding,
least not very well. Only reason I can run your code is having
Perl 5.8 installed on a different drive for 5.8 specific testing.
Use of alarm is not supported on Win32 on earlier Perl versions.

If I could offer more, I would. Recently I removed our Linux operating
system on our dedicated webserver just before bringing it online.

Best I can offer is to suggest you simplify your code as much as possible,
then build from there. Otherwords, strip it to bare bones and test. It is
not uncommon for added complexity to create problems very hard to find.

The sample script is about as simple as I can make it. And it does work
under 5.005.02. I might just pull a copy of 5.8.1 and try that. I think it
has some new capability to switch off the "safe signals" feature which may
have something to do with the problem.

Thanks,
Steve.
 
S

Steve Grazzini

[ waitpid() and safe signals ]
I might just pull a copy of 5.8.1 and try that. I think it has
some new capability to switch off the "safe signals" feature which
may have something to do with the problem.

Right -- %SIG handlers don't use SA_RESTART anymore and there's
some broken (alas!) retry-on-EINTR code in Perl's waitpid().

You can tiptoe around it in 5.8.0 by using POSIX::sigaction()
with SA_RESTART. In 5.8.1 you can get the old unsafe signals
with an environment variable.
 
G

Greg Bacon

What OS? What version of Perl?

With 5.6.1 on Tru64, I get the following output:

Forking pid 605402
Forking pid 597267
Forking pid 638694
Forking pid 636141
Forking pid 612305
Forking pid 646038
Forking pid 525589
Forking pid 655048
pid = 605402, waitpid = 605402
Alarm signal
pid = 597267, waitpid = 597267
pid = 638694, waitpid = 638694
pid = 636141, waitpid = 636141
pid = 612305, waitpid = 612305
pid = 646038, waitpid = 646038
pid = 525589, waitpid = 525589
pid = 655048, waitpid = 655048

With 5.8.0 on Win32, I see

Forking pid -656169
Forking pid -658717
Forking pid -657601
Forking pid -658101
Forking pid -3427937
Forking pid -3426389
Forking pid -3426873
Forking pid -3437929
pid = -656169, waitpid = -656169
Alarm signal
pid = -658717, waitpid = -658717
pid = -657601, waitpid = -657601
pid = -658101, waitpid = -658101
pid = -3427937, waitpid = -3427937
pid = -3426389, waitpid = -3426389
pid = -3426873, waitpid = -3426873
pid = -3437929, waitpid = -3437929

Greg
 
S

Steve East

Steve Grazzini said:
[ waitpid() and safe signals ]
I might just pull a copy of 5.8.1 and try that. I think it has
some new capability to switch off the "safe signals" feature which
may have something to do with the problem.

Right -- %SIG handlers don't use SA_RESTART anymore and there's
some broken (alas!) retry-on-EINTR code in Perl's waitpid().

You can tiptoe around it in 5.8.0 by using POSIX::sigaction()
with SA_RESTART. In 5.8.1 you can get the old unsafe signals
with an environment variable.

I'm trying the tiptoe approach. I replaced the %SIG handler with:

my $sigset = POSIX::SigSet->new(&POSIX::SIGALRM);
my $sigaction = POSIX::SigAction->new('main::alarmist', $sigset,
&POSIX::SA_RESTART);

but now I never seem to get control back from the signal handler. Am I
missing a flag or just totally missing the point?

Steve.
 
S

Steve East

Steve East said:
Steve Grazzini said:
[ waitpid() and safe signals ]
I might just pull a copy of 5.8.1 and try that. I think it has
some new capability to switch off the "safe signals" feature which
may have something to do with the problem.

Right -- %SIG handlers don't use SA_RESTART anymore and there's
some broken (alas!) retry-on-EINTR code in Perl's waitpid().

You can tiptoe around it in 5.8.0 by using POSIX::sigaction()
with SA_RESTART. In 5.8.1 you can get the old unsafe signals
with an environment variable.

I'm trying the tiptoe approach. I replaced the %SIG handler with:

my $sigset = POSIX::SigSet->new(&POSIX::SIGALRM);
my $sigaction = POSIX::SigAction->new('main::alarmist', $sigset,
&POSIX::SA_RESTART);

but now I never seem to get control back from the signal handler. Am I
missing a flag or just totally missing the point?

Forgive my stupidity; I wasn't calling sigaction. I changed the code to:

my $sigset = POSIX::SigSet->new;
my $sigaction = POSIX::SigAction->new('main::alarmist', $sigset,
&POSIX::SA_RESTART);
my $oldaction = POSIX::SigAction->new;
POSIX::sigaction(SIGALRM, $sigaction, $oldaction);

and all is working well. Though I wasn't sure how to setup the old action
parameter. It wouldn't let me undef it.

Thanks,
Steve.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,705
Latest member
Stefkari24

Latest Threads

Top