1 module hunt.concurrency.ThreadLocalRandom;
2 
3 import hunt.concurrency.atomic.AtomicHelper;
4 import hunt.util.DateTime;
5 import std.datetime : Clock;
6 
7 
8 /**
9  * A random number generator isolated to the current thread.  Like the
10  * global {@link java.util.Random} generator used by the {@link
11  * java.lang.Math} class, a {@code ThreadLocalRandom} is initialized
12  * with an internally generated seed that may not otherwise be
13  * modified. When applicable, use of {@code ThreadLocalRandom} rather
14  * than shared {@code Random} objects in concurrent programs will
15  * typically encounter much less overhead and contention.  Use of
16  * {@code ThreadLocalRandom} is particularly appropriate when multiple
17  * tasks (for example, each a {@link ForkJoinTask}) use random numbers
18  * in parallel in thread pools.
19  *
20  * <p>Usages of this class should typically be of the form:
21  * {@code ThreadLocalRandom.current().nextX(...)} (where
22  * {@code X} is {@code Int}, {@code Long}, etc).
23  * When all usages are of this form, it is never possible to
24  * accidentally share a {@code ThreadLocalRandom} across multiple threads.
25  *
26  * <p>This class also provides additional commonly used bounded random
27  * generation methods.
28  *
29  * <p>Instances of {@code ThreadLocalRandom} are not cryptographically
30  * secure.  Consider instead using {@link java.security.SecureRandom}
31  * in security-sensitive applications. Additionally,
32  * default-constructed instances do not use a cryptographically random
33  * seed unless the {@linkplain System#getProperty system property}
34  * {@code java.util.secureRandomSeed} is set to {@code true}.
35  *
36  * @author Doug Lea
37  */
38 class ThreadLocalRandom {
39     // These fields are used to build the high-performance PRNGs in the
40     // concurrent code, and we can not risk accidental false sharing.    
41     /** The current seed for a ThreadLocalRandom */
42     static long threadLocalRandomSeed = 0;
43 
44     /** Probe hash value; nonzero if threadLocalRandomSeed initialized */
45     static int threadLocalRandomProbe = 0;
46 
47     /** Secondary seed isolated from public ThreadLocalRandom sequence */
48     static int threadLocalRandomSecondarySeed = 0;
49 
50     static shared long seeder = 0;
51     
52     /** Generates per-thread initialization/probe field */
53     private static shared int probeGenerator = 0;
54 
55     /**
56      * The seed increment.
57      */
58     private enum long GAMMA = 0x9e3779b97f4a7c15L;
59 
60     /**
61      * The increment for generating probe values.
62      */
63     private enum int PROBE_INCREMENT = 0x9e3779b9;
64 
65     /**
66      * The increment of seeder per new instance.
67      */
68     private enum long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
69 
70     /**
71      * The least non-zero value returned by nextDouble(). This value
72      * is scaled by a random value of 53 bits to produce a result.
73      */
74     private enum double DOUBLE_UNIT = 0x1.0p-53;  // 1.0  / (1L << 53)
75     private enum float  FLOAT_UNIT  = 0x1.0p-24f; // 1.0f / (1 << 24)
76 
77     // IllegalArgumentException messages
78     enum string BAD_BOUND = "bound must be positive";
79     enum string BAD_RANGE = "bound must be greater than origin";
80     enum string BAD_SIZE  = "size must be non-negative";    
81 
82     shared static this() {
83         seeder = mix64(DateTime.currentTimeMillis()) ^
84                          mix64(Clock.currStdTime()*100);
85     }
86 
87     private static long mix64(long z) {
88         z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
89         z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L;
90         return z ^ (z >>> 33);
91     }
92 
93     private static int mix32(long z) {
94         z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
95         return cast(int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);
96     }
97 
98     /**
99      * Initialize Thread fields for the current thread.  Called only
100      * when Thread.threadLocalRandomProbe is zero, indicating that a
101      * thread local seed value needs to be generated. Note that even
102      * though the initialization is purely thread-local, we need to
103      * rely on (static) atomic generators to initialize the values.
104      */
105     static final void localInit() {
106         int p = AtomicHelper.increment(probeGenerator, PROBE_INCREMENT);
107         int probe = (p == 0) ? 1 : p; // skip 0
108         long seed = mix64(AtomicHelper.getAndAdd(seeder, SEEDER_INCREMENT));
109         threadLocalRandomSeed = seed;
110         threadLocalRandomProbe = probe;
111     }
112 
113     /**
114      * Returns the probe value for the current thread without forcing
115      * initialization. Note that invoking ThreadLocalRandom.current()
116      * can be used to force initialization on zero return.
117      */
118     static int getProbe() {
119         return threadLocalRandomProbe;
120     }
121 
122     /**
123      * Pseudo-randomly advances and records the given probe value for the
124      * given thread.
125      */
126     static int advanceProbe(int probe) {
127         probe ^= probe << 13;   // xorshift
128         probe ^= probe >>> 17;
129         probe ^= probe << 5;
130         threadLocalRandomProbe = probe;
131         return probe;
132     }    
133 
134     /**
135      * Returns the pseudo-randomly initialized or updated secondary seed.
136      */
137     static int nextSecondarySeed() {
138         int r;
139         if ((r = threadLocalRandomSecondarySeed) != 0) {
140             r ^= r << 13;   // xorshift
141             r ^= r >>> 17;
142             r ^= r << 5; 
143         }
144         else if ((r = mix32(AtomicHelper.getAndAdd(seeder, SEEDER_INCREMENT))) == 0)
145             r = 1; // avoid zero
146         threadLocalRandomSecondarySeed = r;
147         return r;
148     }
149 }