Automatically call srand() before rand() if user didn't
Chip Salzenberg [Wed, 19 Feb 1997 19:04:03 +0000 (07:04 +1200)]
pod/perlfunc.pod
pp.c

index 9d21587..b3cf4e4 100644 (file)
@@ -2275,8 +2275,8 @@ If EXPR is omitted, uses $_.
 
 Returns a random fractional number between 0 and the value of EXPR.
 (EXPR should be positive.)  If EXPR is omitted, returns a value between 
-0 and 1.  This function produces repeatable sequences unless srand() 
-is invoked.  See also srand().
+0 and 1.  Automatically calls srand() unless srand() has already been
+called.  See also srand().
 
 (Note: if your rand function consistently returns numbers that are too
 large or too small, then your version of Perl was probably compiled
@@ -3036,17 +3036,23 @@ root of $_.
 
 =item srand EXPR
 
-Sets the random number seed for the C<rand> operator.  If EXPR is omitted,
-uses a semi-random value based on the current time and process ID, among
-other things.  In versions of Perl prior to 5.004 the default seed was
-just the current time().  This isn't a particularly good seed, so many
-old programs supply their own seed value (often C<time ^ $$> or C<time ^
-($$ + ($$ << 15))>), but that isn't necessary any more.
-
-You need something much more random than the default seed for
-cryptographic purposes, though.  Checksumming the compressed output of
-one or more rapidly changing operating system status programs is the
-usual method. For example:
+=item srand
+
+Sets the random number seed for the C<rand> operator.  If EXPR is
+omitted, uses a semi-random value based on the current time and process
+ID, among other things.  In versions of Perl prior to 5.004 the default
+seed was just the current time().  This isn't a particularly good seed,
+so many old programs supply their own seed value (often C<time ^ $$> or
+C<time ^ ($$ + ($$ << 15))>), but that isn't necessary any more.
+
+In fact, it's usually not necessary to call srand() at all, because if
+it is not called explicitly, it is called implicitly at the first use of
+the C<rand> operator.
+
+However, you need something much more random than the default seed for
+cryptographic purposes.  Checksumming the compressed output of one or
+more rapidly changing operating system status programs is the usual
+method. For example:
 
     srand (time ^ $$ ^ unpack "%L*", `ps axww | gzip`);
 
diff --git a/pp.c b/pp.c
index 7859606..62a01ec 100644 (file)
--- a/pp.c
+++ b/pp.c
 typedef int IBW;
 typedef unsigned UBW;
 
-static SV* refto _((SV* sv));
 static void doencodes _((SV* sv, char* s, I32 len));
+static SV* refto _((SV* sv));
+static U32 seed _((void));
+
+static bool srand_called = FALSE;
 
 /* variations on pp_null */
 
@@ -1296,6 +1299,10 @@ PP(pp_rand)
        value = POPn;
     if (value == 0.0)
        value = 1.0;
+    if (!srand_called) {
+       (void)srand((unsigned)seed());
+       srand_called = TRUE;
+    }
 #if RANDBITS == 31
     value = rand() * value / 2147483648.0;
 #else
@@ -1316,38 +1323,44 @@ PP(pp_rand)
 PP(pp_srand)
 {
     dSP;
-    I32 anum;
+    UV anum;
+    if (MAXARG < 1)
+       anum = seed();
+    else
+       anum = POPu;
+    (void)srand((unsigned)anum);
+    srand_called = TRUE;
+    EXTEND(SP, 1);
+    RETPUSHYES;
+}
 
-    if (MAXARG < 1) {
+static U32
+seed()
+{
+    U32 u;
 #ifdef VMS
 #  include <starlet.h>
-       unsigned int when[2];
-       _ckvmssts(sys$gettim(when));
-       anum = when[0] ^ when[1];
+    unsigned int when[2];
+    _ckvmssts(sys$gettim(when));
+    u = when[0] ^ when[1];
 #else
 #  ifdef HAS_GETTIMEOFDAY
-       struct timeval when;
-       gettimeofday(&when,(struct timezone *) 0);
-       anum = when.tv_sec ^ when.tv_usec;
+    struct timeval when;
+    gettimeofday(&when,(struct timezone *) 0);
+    u = when.tv_sec ^ when.tv_usec;
 #  else
-       Time_t when;
-       (void)time(&when);
-       anum = when;
+    Time_t when;
+    (void)time(&when);
+    u = when;
 #  endif
 #endif
-#if !defined(PLAN9) /* XXX Plan9 assembler chokes on this; fix coming soon  */
-                    /*     17-Jul-1996  bailey@genetics.upenn.edu           */
-       /* What is a good hashing algorithm here? */
-       anum ^= (  (  269 * (U32)getpid())
-                ^ (26107 * (U32)&when)
-                ^ (73819 * (U32)stack_sp));
+#ifndef PLAN9          /* XXX Plan9 assembler chokes on this; fix needed  */
+    /* What is a good hashing algorithm here? */
+    u ^= (   (  269 * (U32)getpid())
+          ^ (26107 * (U32)&when)
+          ^ (73819 * (U32)stack_sp));
 #endif
-    }
-    else
-       anum = POPi;
-    (void)srand(anum);
-    EXTEND(SP, 1);
-    RETPUSHYES;
+    return u;
 }
 
 PP(pp_exp)