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 }