Tcl Source Code

View Ticket
Login
Ticket UUID: 416643
Title: rand() has identical results in threads.
Type: Bug Version: obsolete: 8.4a2
Submitter: davygrvy Created on: 2001-04-17 11:27:49
Subsystem: 47. Bytecode Compiler Assigned To: msofer
Priority: 5 Medium Severity:
Status: Closed Last Modified: 2001-05-08 05:16:08
Resolution: Fixed Closed By: msofer
    Closed on: 2001-05-07 22:16:08
Description:
This is having difficulty on windows.  Isn't the c-
runtime supposed to handle this situation just fine?  
I'm perplexed by this:


package require Thread

foreach a {1 2 3 4 5 6} {
   set threads($a) [thread::create { proc doit {} { 
puts [expr {rand()}] }; thread::wait }]
}

foreach a {1 2 3 4 5 6} {
   thread::send -async $threads($a) doit
}

foreach a {1 2 3 4 5 6} {
   thread::send -async $threads($a) thread::exit
}


C:\Program Files\Tcl\bin>tclsh84t
% source test.tcl
% 0.0541875106535
0.0541875106535
0.0541875106535
0.0541875106535
0.0541875106535
0.0541875106535


I really don't think this is a Tcl inherit to Tcl, but 
does show an interesting behavior.  Changing the run 
to be syncronous got this:

package require Thread

foreach a {1 2 3 4 5 6} {
   thread::create { puts [expr {rand()}] }
}

% source test.tcl
0.11639496317
0.366838779471
0.366838779471
0.484234318363
0.609456226513
% 0.726851765405


The docs state "Each interpreter has it's own seed." 
on the expr man page, but I tend to disagree after 
seeing the above.  Where's the seeding happening?  
Should Tcl_CreateInterp() do the seeding?
User Comments: msofer added on 2001-05-08 05:16:08:
Logged In: YES 
user_id=148712

Patch committed.

dgp added on 2001-04-30 01:35:18:
Logged In: YES 
user_id=80530

The patch looks OK to me.  I think I would add
a more detailed explanation in the comment about
why the Tcl_GetCurrentThread call is there, maybe
even a link back to this bug report.

I'm assigning this to the proper Category for final
maintainer review and commit.

davygrvy added on 2001-04-26 08:01:39:
Logged In: YES 
user_id=7549

Don,

I hand this to you.  I can handle this from script anyways, 
so it's no rush.

davygrvy added on 2001-04-21 05:36:17:
Logged In: YES 
user_id=7549

This seems to work Ok, too.

iPtr->randSeed = TclpGetClicks() ^ ((long) 
Tcl_GetCurrentThread() << ((TclpGetClicks() % 10) + 17));

davygrvy added on 2001-04-20 10:20:41:

File Added - 5549: rand_patch.txt

davygrvy added on 2001-04-20 08:49:36:
Logged In: YES 
user_id=7549

Don,

I'm happy with the attached patch.  These are the results 
for me (on windows) running the first code snippit:

source test2.tcl
% 0.928865028046
0.515779082904
0.648316936404
0.315270977707
0.826455665671
0.931451399779
source test2.tcl
% 0.878650014232
0.370051698
0.808093390804
0.0654099826074
0.547328303357
0.958690000679
source test2.tcl
% 0.210988179879
0.774670734897
0.41666902854
0.185170308307
0.105992445772
0.954533711986
info patch
8.4a3
%

If I remember correctly, Thread IDs on UNIX have smaller 
numbers then on windows and might not effectively shift 
TclpGetClicks deeply enough.  I'll leave the math up 
someone more qualified than myself.

davygrvy added on 2001-04-18 06:15:59:
Logged In: YES 
user_id=7549

Say I do update and rebuild for the HEAD, and all threads 
do hit line 4087, tclExecute.c:

iPtr->randSeed = TclpGetClicks();

and for the sake of discussion, TclpGetClicks() returns the 
exact same result for all threads running in parallel.  
Even with a higher resolution of the timer, the 
*possibility* for a same seed value still exists.

Would it make sense to have the thread extension do this 
for me by pre-seeding so even if parallel threads call rand
() at the exact same moment, the return will be different?

dgp added on 2001-04-18 05:13:28:
Logged In: YES 
user_id=80530

Ah.  I see you report this on Windows.  Do you still see
this behavior using the latest CVS HEAD sources (8.4a3)?
Since 8.4a2 was released, Kevin Kenny enhanced TclpGetTime
to get much finer "clicks" resolution on Windows.  That
might take care of this.

If you're really using 8.4a2, that rules out the new
rand() code too, since it was added since then.

dgp added on 2001-04-18 04:52:23:
Logged In: YES 
user_id=80530

Since I just rewrote rand() for Tcl 8.4a2 and Tcl 8.3.3,
I want to find out whether this is something new I broke
or not.

davygrvy added on 2001-04-18 04:49:03:
Logged In: YES 
user_id=7549

I haven't checked 8.3.2 or 8.3.3, yet.

It could be possible that all 6 threads hit this bit of 
code at the exact same time (line 4087, tclExecute.c):

iPtr->randSeed = TclpGetClicks();

I leave this up to you guys.  I'm not sure if this is 
really a "bug" or not.  I can send all the threads a seed 
when I start them.  I can get around the behavior in 
script.  I don't know if this warrants a sluething.

dgp added on 2001-04-18 03:07:37:
Logged In: YES 
user_id=80530

Is this a new problem, or does it also exist in Tcl 8.3.2?

Tcl has it's own rand() implementation -- it does not call
a system rand() -- so this problem is a problem with Tcl.

There is one seed per interp: iPtr->randSeed, and it is
initialized in ExprRandFunc() in generic/tclExecute.c.

davygrvy added on 2001-04-17 18:31:21:
Logged In: YES 
user_id=7549

wait a sec...  2 of those in the syncronous test were 
identical.  This is interesting :)

Attachments: