When rand isn't random

In spite of having programming Perl mostly full time for the last 12 years, I still find myself learning new things about how Perl works (like the time I discovered the arcane apostrophe package separator when trying to add a possessive 's' on the end of a variable in an interpolated string).

Yesterday yielded a similar epiphany when I realized how rand, srand and fork were interacting in our e-commerce application at $work. Consider the following one-liner:

perl -le 'for(1..2) { fork or do { print int rand 100; exit } }'

This prints two random numbers between 0 and 99. Simple. Now, consider this slightly different example:

perl -le 'rand; for(1..2) { fork or do { print int rand 100; exit } }'

This prints the same random number between 0 and 99 twice. The reasoning is quite simple but was not obvious to me at first. When rand is first called in a Perl application, srand is implicitly called to seed rand with a different starting number. If this happens prior to a process forking, the same seed is shared by all of the child processes and non-randomness ensues.

The tricky bit to this issue is that since the rand seed is effectively global, a pre-fork rand call can be anywhere in your large-ish legacy mod_perl application and it won't be apparent for quite some time why the "random" numbers being generated by the mod_perl children seem to be colliding at an alarming rate! This issue was further occluded by the fact that the srand perldoc assures the reader (I'm a very trusting reader) that most applications never need to call srand explicitly.

In my opinion, one of two things should be changed about how this works. Either:

  1. The global has-rand-been-seeded flag should be per-process, or
  2. A warning should be added to the srand perldoc page

In our case, simply adding a PerlChildInitHandler that re-calls srand proved to be a very simple fix. This solution comes courtesy of bugzilla which was quite google-able once one gets past the denial and realizes that fork and srand interact differently than one thought:

# In your Apache configuration
PerlChildInitHandler "sub { srand }"

What do you think? Is this worthy of an srand doc patch or a functionality change or am I the last Perl developer to uncover this behavior?

6 Comments

Submit a patch.

It's really subtle and it actually *is* a specific case where an application should use srand.

See the POD for File::Temp for some documentation on this behavior.

Am I missing something or should that instead be:

PerlChildInitHandler "sub { srand }"

Notice the "srand" instead of "rand" call. Otherwise if some library you're using during startup (or preload) makes a call to rand() then you're still sharing the same seed, right?

Ack, bit by this in mod_perl 2.0.8! The PerlChildInitHandler workaround seems to solve my problem.

Leave a comment

About Brian Phillips

user-pic I blog about Perl.