Improve the randomness of ThreadLocalRandom for all platform
- Fixes #1765 Java 6 did a poor job of generating seedUniquifier unlike 7, so I implemented platform-independent seedUniquifier generator with configurability
This commit is contained in:
parent
9ca20b73d3
commit
8142aae765
@ -22,7 +22,15 @@
|
||||
|
||||
package io.netty.util.internal;
|
||||
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* A random number generator isolated to the current thread. Like the
|
||||
@ -50,6 +58,90 @@ import java.util.Random;
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public class ThreadLocalRandom extends Random {
|
||||
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ThreadLocalRandom.class);
|
||||
|
||||
private static final AtomicLong seedUniquifier = new AtomicLong();
|
||||
|
||||
private static volatile long initialSeedUniquifier;
|
||||
|
||||
public static void setInitialSeedUniquifier(long initialSeedUniquifier) {
|
||||
ThreadLocalRandom.initialSeedUniquifier = initialSeedUniquifier;
|
||||
}
|
||||
|
||||
public static synchronized long getInitialSeedUniquifier() {
|
||||
// Use the value set via the setter.
|
||||
long initialSeedUniquifier = ThreadLocalRandom.initialSeedUniquifier;
|
||||
if (initialSeedUniquifier == 0) {
|
||||
// Use the system property value.
|
||||
ThreadLocalRandom.initialSeedUniquifier = initialSeedUniquifier =
|
||||
SystemPropertyUtil.getLong("io.netty.initialSeedUniquifier", 0);
|
||||
}
|
||||
|
||||
// Otherwise, generate one.
|
||||
if (initialSeedUniquifier == 0) {
|
||||
// Try to generate a real random number from /dev/random.
|
||||
// Get from a different thread to avoid blocking indefinitely on a machine without much entrophy.
|
||||
final BlockingQueue<Long> queue = new LinkedBlockingQueue<Long>();
|
||||
Thread generatorThread = new Thread("initialSeedUniquifierGenerator") {
|
||||
@Override
|
||||
public void run() {
|
||||
SecureRandom random = new SecureRandom(); // Get the real random seed from /dev/random
|
||||
queue.add(random.nextLong());
|
||||
}
|
||||
};
|
||||
generatorThread.start();
|
||||
|
||||
// Get the random seed from the thread with timeout.
|
||||
final long timeoutSeconds = 3;
|
||||
final long deadLine = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds);
|
||||
for (;;) {
|
||||
long waitTime = deadLine - System.nanoTime();
|
||||
if (waitTime <= 0) {
|
||||
logger.warn(
|
||||
"Failed to get the secure random number from SecureRandom within {} seconds. " +
|
||||
"Not enough entrophy?", timeoutSeconds);
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
Long result = queue.poll(waitTime, TimeUnit.NANOSECONDS);
|
||||
if (result != null) {
|
||||
initialSeedUniquifier = result;
|
||||
break;
|
||||
}
|
||||
} catch (InterruptedException ignore) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
// Just in case the initialSeedUniquifier is zero or some other constant
|
||||
initialSeedUniquifier ^= 0x3255ecdc33bae119L; // just a meaningless random number
|
||||
initialSeedUniquifier ^= Long.reverse(System.nanoTime());
|
||||
|
||||
ThreadLocalRandom.initialSeedUniquifier = initialSeedUniquifier;
|
||||
}
|
||||
|
||||
return initialSeedUniquifier;
|
||||
}
|
||||
|
||||
private static long newSeed() {
|
||||
for (;;) {
|
||||
final long current = seedUniquifier.get();
|
||||
final long actualCurrent = current != 0? current : getInitialSeedUniquifier();
|
||||
|
||||
// L'Ecuyer, "Tables of Linear Congruential Generators of Different Sizes and Good Lattice Structure", 1999
|
||||
final long next = actualCurrent * 181783497276652981L;
|
||||
|
||||
if (seedUniquifier.compareAndSet(current, next)) {
|
||||
if (current == 0 && logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("-Dio.netty.initialSeedUniquifier: 0x%016x", actualCurrent));
|
||||
}
|
||||
return next ^ System.nanoTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// same constants as Random, but must be redeclared because private
|
||||
private static final long multiplier = 0x5DEECE66DL;
|
||||
private static final long addend = 0xBL;
|
||||
@ -73,24 +165,24 @@ public class ThreadLocalRandom extends Random {
|
||||
// each other.
|
||||
private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
|
||||
|
||||
/**
|
||||
* The actual ThreadLocal
|
||||
*/
|
||||
private static final ThreadLocal<ThreadLocalRandom> localRandom =
|
||||
new ThreadLocal<ThreadLocalRandom>() {
|
||||
protected ThreadLocalRandom initialValue() {
|
||||
return new ThreadLocalRandom();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor called only by localRandom.initialValue.
|
||||
*/
|
||||
ThreadLocalRandom() {
|
||||
super();
|
||||
super(newSeed());
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual ThreadLocal
|
||||
*/
|
||||
private static final ThreadLocal<ThreadLocalRandom> localRandom =
|
||||
new ThreadLocal<ThreadLocalRandom>() {
|
||||
protected ThreadLocalRandom initialValue() {
|
||||
return new ThreadLocalRandom();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current thread's {@code ThreadLocalRandom}.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user