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