problem with rand on OSX

C

C. A.

I expect each child to have a different $slp, but they are identical:

=8<================
#! /usr/bin/perl -w

use strict;

my ( $pid, @jobs, $n );

$|++;

for (0..2)
{
# three children, we'll need their pid's later...
$pid = fork();
last if $pid == 0;
push @jobs, $pid;
}

if ( $pid != 0 ) {
$SIG{CHLD} = 'IGNORE';
} else {
my $slp = int rand 20;

print "child gets $slp second(s)\n";
exit 0;
}
=8<================

uname -r -v -m
7.3.0 Darwin Kernel Version 7.3.0: Fri Mar 5 14:22:55 PST 2004;\
root:xnu/xnu-517.3.15.obj~4/RELEASE_PPC Power Macintosh
perl --version

This is perl, v5.8.1-RC3 built for darwin-thread-multi-2level
(with 1 registered patch, see perl -V for more detail)

Yours,

CA
--
 
E

Eric Schwartz

C. A. said:
I expect each child to have a different $slp, but they are identical:

Each child has a different $slp on my system. Also, your code hurts
my brane, because of the following:
=8<================
#! /usr/bin/perl -w

You should 'use warnings;' instead of using -w; this doesn't hurt my
brane, but it's good advice nonetheless.
use strict;

my ( $pid, @jobs, $n );

This is the start of the pain. $pid is effectively global to this
program, but doesn't need to be.
$|++;

for (0..2)
{
# three children, we'll need their pid's later...
$pid = fork();
last if $pid == 0;

This is bad, because you never check if fork() fails, and kinda weird,
because the children explicitly break out of the loop with $pid set to
0; my mental model of such loops has $pid being defined locally to the
loop, on the grounds that the child doesn't care, and the parent
already has them in @jobs.

$pid is used in a loop in the parent, but at the end of the loop, it's
really a flag meaning, "am I the parent or not?". It'd be clearer to
just set a $parent flag for that instead of using the same variable
for two disparate purposes.

Me, I'd rewrite it something like:

defined(my $pid = fork()) or die "Couldn't fork: $!";
do_childstuff() unless $pid;

and put the stuff in the else clause below into a sub 'do_childstuff'.
push @jobs, $pid;
}

if ( $pid != 0 ) {

This would read more clearly if it were

if ($parent) {

or even

if (@jobs) {
$SIG{CHLD} = 'IGNORE';
} else {
my $slp = int rand 20;

print "child gets $slp second(s)\n";
exit 0;
}

-=Eric
 
C

C. A.

You should 'use warnings;' instead of using -w; this doesn't hurt my
brane, but it's good advice nonetheless.

This is irrelevant to my question, but why do you prefer use warnings to -w?

Yours,

CA
--
 
C

C. A.

Each child has a different $slp on my system. Also, your code hurts
my brane, because of the following:

This is important... this is the topic of my post. I'm running this on
a G5, if you are not you are off-topic.
This is the start of the pain. $pid is effectively global to this
program, but doesn't need to be.

Thanks for the advice... in Real Life(TM) does resemble your suggestions
to a greater extent, I'm giving you something i threw together quickly
to demonstrate what I believe to be a problem with Perl.

Sorry my post (and code) were not a little clearer.

Yours,

CA
--
 
D

David H. Adler

This is irrelevant to my question, but why do you prefer use warnings to -w?

For one thing the warnings pragma is more flexible than -w. On the
other hand, if you're using a sufficiently old version of perl, it does
not exist.

dha
 
C

C. A.

This would read more clearly if it were

if ($parent) {

parent is a better mnemonic in both the parent and the child, I'll agree.
The fact it's value is the process id of the child (in the parent) could
be considered a side effect?
or even

if (@jobs) {

@jobs is not empty in two of the three children.

Yours,

CA
--
 
C

C. A.

Sorry my post (and code) were not a little clearer.

Perlbug is not set up on that machine, and I haven't seen this issue in the
(okay, somewhat meager) search of the archives.

Here's something to exercise the bug you might find more readable:

=8<=======
#! /usr/bin/perl -w

use strict;

my @jobs;

for (0..2)
{
defined( my $pid = fork() ) or die "my BABY!!!";;
last if $pid == 0;
push @jobs, $pid;
}

if ( 3 == @jobs ) {
$SIG{CHLD} = 'IGNORE';
sleep 1;
} else {
my $slp = int rand 20;

print "child gets $slp second(s)\n";
exit 0;
}
=8<=======

I think this means rand MUST use something as simple as the unix time
in seconds as a seed. How else could three independent processes get
the same random number?

Shall I post the perbug output?

Yours,

CA
 
E

Eric Schwartz

C. A. said:
@jobs is not empty in two of the three children.

Ack, that's another reason I dislike that structure-- it preserves
information in the children that doesn't really need to be there. But
as long as your real code doesn't look like that, I'm okay with it.

-=Eric
 
A

Anno Siegel

C. A. said:
I expect each child to have a different $slp, but they are identical:

Perl initializes its random generator once at startup. The kids inherit
the interpreter, complete with initialized random generator, so the
random sequence is the same in all kids. Call srand() in each of the kids.

Anno
 
C

C. A.

Perl initializes its random generator once at startup. The kids inherit
the interpreter, complete with initialized random generator, so the
random sequence is the same in all kids. Call srand() in each of the kids.

That doesn't seem to hold for either Sun/sparc or Linux/P4. I'm surprised
by the difference. Do all the BSDs have this behavior?
 
C

C. A.

C. A. said:
[fork & rand on OSX]

Perl initializes its random generator once at startup. The kids inherit
the interpreter, complete with initialized random generator, so the
random sequence is the same in all kids. Call srand() in each of the kids.

Perl porters says this is fixed on 5.8.4 (that srand isn't called until rand
is), but someone told me it still has this behavior in OpenBSD, even
on 5.8.4.bleeding.

Yours,

Charles
--
 
C

C. A.

I expect each child to have a different $slp, but they are identical:

from perlbug:

-----Forwarded Message-----

From: Dominic Dunlop <[email protected]>
To: (e-mail address removed)
Cc: C. Abney (via RT) <[email protected]>
Subject: Re: [perl #30028] rand broken on Mac OSX (G5), was Re: problem with~
Date: 03 Jun 2004 16:59:11 +0200

Summary: fixed in 5.8.4.

#! /usr/bin/perl -w

use strict;

my @jobs;

for (0..2)
{
defined( my $pid = fork() ) or die "my BABY!!!";;
last if $pid == 0;
push @jobs, $pid;
}

if ( 3 == @jobs ) {
$SIG{CHLD} = 'IGNORE';
sleep 1;
} else {
my $slp = int rand 20;

print "child gets $slp second(s)\n";
exit 0;
}
Well, on my Mac OS 10.3.4 (with a G3 processor) that indeed gives

child gets 6 second(s)
child gets 6 second(s)
child gets 6 second(s)

(or similar) with perl5.8.1rc3 (as shipped with Mac OS) or with a
freshly-built perl5.8.1. Given that the random number seed is stored in
memory that gets cloned from the parent to each child, and each child
applies the same algorithm to the seed to get the next random number,
this is the behaviour I'd expect. However, I get

child gets 19 second(s)
child gets 11 second(s)
child gets 0 second(s)

(or similar) with perl5.8.4, the current stable release, and with 5.9.2
on the development track.

Both perls are using the same random number generator:
$ perl '-V:rand.*' # either perl version
randbits='48'
randfunc='drand48'
random_r_proto='0'
randseedtype='long'

What seems to have changed is where the random number generator seed
gets set: in 5.8.1, it appears to be in the parent before any call to
rand(), so each child gets the same seed; in 5.8.4 the setting seems to
be delayed until the first use of rand() -- so each child gets a
different seed. Adding a line

rand;

near the top of the script to force the setting of the seed in the
parent brings back the 5.8.1 behaviour.
I can't see a patch between 5.8.1 and 5.8.4 which claims to have
anything to do with random numbers so I don't know what changed the
behaviour. Can anybody shed any light?

However, your solution is to upgrade to 5.8.4.

[schibble]
 
D

David H. Adler

[...]
For one thing the warnings pragma is more flexible than -w. On the
other hand, if you're using a sufficiently old version of perl, it does
not exist.

With 5.8.0 you could switch off -w dervived warnings using no warnings
'blah'; just as effectively as if you used use warnings; And -w takes
only two keypresses.

One might want to check out the section "What's wrong with -w and $^W"
in the perllexwarn page. There may be some subtle bugs floating around
with this method.

dha
 

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

Members online

No members online now.

Forum statistics

Threads
474,161
Messages
2,570,891
Members
47,423
Latest member
henerygril

Latest Threads

Top