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;
|
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.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
|
* A random number generator isolated to the current thread. Like the
|
||||||
@ -50,6 +58,90 @@ import java.util.Random;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public class ThreadLocalRandom extends Random {
|
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
|
// same constants as Random, but must be redeclared because private
|
||||||
private static final long multiplier = 0x5DEECE66DL;
|
private static final long multiplier = 0x5DEECE66DL;
|
||||||
private static final long addend = 0xBL;
|
private static final long addend = 0xBL;
|
||||||
@ -73,24 +165,24 @@ public class ThreadLocalRandom extends Random {
|
|||||||
// each other.
|
// each other.
|
||||||
private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
|
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.
|
* Constructor called only by localRandom.initialValue.
|
||||||
*/
|
*/
|
||||||
ThreadLocalRandom() {
|
ThreadLocalRandom() {
|
||||||
super();
|
super(newSeed());
|
||||||
initialized = true;
|
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}.
|
* Returns the current thread's {@code ThreadLocalRandom}.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user