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 }