Stop ThreadLocalRandom's initial seed generation immediately on interruption

Motivation:

ThreadLocalRandomTest reveals that ThreadLocalRandom's initial seed generation loop becomes tight if the thread is interrupted.
We currently interrupt ourselves inside the wait loop, which will raise an InterruptedException again in the next iteration, resulting in infinite (up to 3 seconds) exception construction and thread interruptions.

Modification:

- When the initial seed generator thread is interrupted, break out of the wait loop immediately.
- Log properly when the initial seed generation failed due to interruption.
- When failed to generate the initial seed, interrupt the generator thread just in case the SecureRandom implementation handles it properly.
- Make the initial seed generator thread daemon and handle potential exceptions raised due to the interruption.

Result:

No more tight loop on interruption.  More robust generator thread termination. Fixes #2412
This commit is contained in:
Trustin Lee 2014-04-20 17:54:57 +09:00
parent af9ab8b370
commit a31f36d933

View File

@ -25,6 +25,7 @@ package io.netty.util.internal;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.Thread.UncaughtExceptionHandler;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Random; import java.util.Random;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
@ -90,17 +91,27 @@ public class ThreadLocalRandom extends Random {
queue.add(random.generateSeed(8)); queue.add(random.generateSeed(8));
} }
}; };
generatorThread.setDaemon(true);
generatorThread.start(); generatorThread.start();
generatorThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
logger.debug("An exception has been raised by {}", t.getName(), e);
}
});
// Get the random seed from the thread with timeout. // Get the random seed from the thread with timeout.
final long timeoutSeconds = 3; final long timeoutSeconds = 3;
final long deadLine = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds); final long deadLine = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds);
boolean interrupted = false;
for (;;) { for (;;) {
long waitTime = deadLine - System.nanoTime(); long waitTime = deadLine - System.nanoTime();
if (waitTime <= 0) { if (waitTime <= 0) {
generatorThread.interrupt();
logger.warn( logger.warn(
"Failed to generate a seed from SecureRandom within {} seconds. " + "Failed to generate a seed from SecureRandom within {} seconds. " +
"Not enough entrophy?", timeoutSeconds); "Not enough entrophy?", timeoutSeconds
);
break; break;
} }
@ -119,8 +130,10 @@ public class ThreadLocalRandom extends Random {
break; break;
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
// restore interrupt status because we don't know how to/don't need to handle it here interrupted = true;
Thread.currentThread().interrupt(); generatorThread.interrupt();
logger.warn("Failed to generate a seed from SecureRandom due to an InterruptedException.");
break;
} }
} }
@ -129,6 +142,11 @@ public class ThreadLocalRandom extends Random {
initialSeedUniquifier ^= Long.reverse(System.nanoTime()); initialSeedUniquifier ^= Long.reverse(System.nanoTime());
ThreadLocalRandom.initialSeedUniquifier = initialSeedUniquifier; ThreadLocalRandom.initialSeedUniquifier = initialSeedUniquifier;
if (interrupted) {
// restore interrupt status because we don't know how to/don't need to handle it here
Thread.currentThread().interrupt();
}
} }
return initialSeedUniquifier; return initialSeedUniquifier;