diff --git a/common/src/main/java/io/netty/util/internal/ThreadLocalRandom.java b/common/src/main/java/io/netty/util/internal/ThreadLocalRandom.java index 7fa15aa627..963f90bc58 100644 --- a/common/src/main/java/io/netty/util/internal/ThreadLocalRandom.java +++ b/common/src/main/java/io/netty/util/internal/ThreadLocalRandom.java @@ -64,59 +64,76 @@ public final class ThreadLocalRandom extends Random { private static final AtomicLong seedUniquifier = new AtomicLong(); - private static volatile long initialSeedUniquifier; + private static volatile long initialSeedUniquifier = + SystemPropertyUtil.getLong("io.netty.initialSeedUniquifier", 0); - public static void setInitialSeedUniquifier(long initialSeedUniquifier) { - ThreadLocalRandom.initialSeedUniquifier = initialSeedUniquifier; - } + private static final Thread seedGeneratorThread; + private static final BlockingQueue seedQueue; + private static final long seedGeneratorStartTime; + private static volatile long seedGeneratorEndTime; - 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. + static { 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 queue = new LinkedBlockingQueue(); - Thread generatorThread = new Thread("initialSeedUniquifierGenerator") { + seedGeneratorThread = new Thread("initialSeedUniquifierGenerator") { @Override public void run() { - SecureRandom random = new SecureRandom(); // Get the real random seed from /dev/random - queue.add(random.generateSeed(8)); + final SecureRandom random = new SecureRandom(); // Get the real random seed from /dev/random + final byte[] seed = random.generateSeed(8); + seedGeneratorEndTime = System.nanoTime(); + seedQueue.add(seed); } }; - generatorThread.setDaemon(true); - generatorThread.start(); - generatorThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { + seedGeneratorThread.setDaemon(true); + seedGeneratorThread.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. + seedQueue = new LinkedBlockingQueue(); + seedGeneratorStartTime = System.nanoTime(); + seedGeneratorThread.start(); + } else { + seedGeneratorThread = null; + seedQueue = null; + seedGeneratorStartTime = 0L; + } + } + + public static void setInitialSeedUniquifier(long initialSeedUniquifier) { + ThreadLocalRandom.initialSeedUniquifier = initialSeedUniquifier; + } + + public static long getInitialSeedUniquifier() { + // Use the value set via the setter. + long initialSeedUniquifier = ThreadLocalRandom.initialSeedUniquifier; + if (initialSeedUniquifier != 0) { + return initialSeedUniquifier; + } + + synchronized (ThreadLocalRandom.class) { + initialSeedUniquifier = ThreadLocalRandom.initialSeedUniquifier; + if (initialSeedUniquifier != 0) { + return initialSeedUniquifier; + } + + // Get the random seed from the generator thread with timeout. final long timeoutSeconds = 3; - final long deadLine = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds); + final long deadLine = seedGeneratorStartTime + TimeUnit.SECONDS.toNanos(timeoutSeconds); boolean interrupted = false; for (;;) { - long waitTime = deadLine - System.nanoTime(); - if (waitTime <= 0) { - generatorThread.interrupt(); - logger.warn( - "Failed to generate a seed from SecureRandom within {} seconds. " + - "Not enough entrophy?", timeoutSeconds - ); - break; - } - + final long waitTime = deadLine - System.nanoTime(); try { - byte[] seed = queue.poll(waitTime, TimeUnit.NANOSECONDS); + final byte[] seed; + if (waitTime <= 0) { + seed = seedQueue.poll(); + } else { + seed = seedQueue.poll(waitTime, TimeUnit.NANOSECONDS); + } + if (seed != null) { initialSeedUniquifier = ((long) seed[0] & 0xff) << 56 | @@ -126,7 +143,7 @@ public final class ThreadLocalRandom extends Random { ((long) seed[4] & 0xff) << 24 | ((long) seed[5] & 0xff) << 16 | ((long) seed[6] & 0xff) << 8 | - (long) seed[7] & 0xff; + (long) seed[7] & 0xff; break; } } catch (InterruptedException e) { @@ -134,6 +151,15 @@ public final class ThreadLocalRandom extends Random { logger.warn("Failed to generate a seed from SecureRandom due to an InterruptedException."); break; } + + if (waitTime <= 0) { + seedGeneratorThread.interrupt(); + logger.warn( + "Failed to generate a seed from SecureRandom within {} seconds. " + + "Not enough entrophy?", timeoutSeconds + ); + break; + } } // Just in case the initialSeedUniquifier is zero or some other constant @@ -148,15 +174,18 @@ public final class ThreadLocalRandom extends Random { // Interrupt the generator thread if it's still running, // in the hope that the SecureRandom provider raises an exception on interruption. - generatorThread.interrupt(); + seedGeneratorThread.interrupt(); } - } - return initialSeedUniquifier; + if (seedGeneratorEndTime == 0) { + seedGeneratorEndTime = System.nanoTime(); + } + + return initialSeedUniquifier; + } } private static long newSeed() { - final long startTime = System.nanoTime(); for (;;) { final long current = seedUniquifier.get(); final long actualCurrent = current != 0? current : getInitialSeedUniquifier(); @@ -166,9 +195,14 @@ public final class ThreadLocalRandom extends Random { if (seedUniquifier.compareAndSet(current, next)) { if (current == 0 && logger.isDebugEnabled()) { - logger.debug(String.format( - "-Dio.netty.initialSeedUniquifier: 0x%016x (took %d ms)", - actualCurrent, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime))); + if (seedGeneratorEndTime != 0) { + logger.debug(String.format( + "-Dio.netty.initialSeedUniquifier: 0x%016x (took %d ms)", + actualCurrent, + TimeUnit.NANOSECONDS.toMillis(seedGeneratorEndTime - seedGeneratorStartTime))); + } else { + logger.debug(String.format("-Dio.netty.initialSeedUniquifier: 0x%016x", actualCurrent)); + } } return next ^ System.nanoTime(); } diff --git a/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java b/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java index c98a050bf7..3f1a6d2303 100644 --- a/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java +++ b/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java @@ -15,6 +15,8 @@ */ package io.netty.util.internal.logging; +import io.netty.util.internal.ThreadLocalRandom; + /** * Creates an {@link InternalLogger} or changes the default factory * implementation. This factory allows you to choose what logging framework @@ -31,9 +33,20 @@ package io.netty.util.internal.logging; * as possible and shouldn't be called more than once. */ public abstract class InternalLoggerFactory { + private static volatile InternalLoggerFactory defaultFactory = newDefaultFactory(InternalLoggerFactory.class.getName()); + static { + // Initiate some time-consuming background jobs here, + // because this class is often initialized at the earliest time. + try { + Class.forName(ThreadLocalRandom.class.getName(), true, InternalLoggerFactory.class.getClassLoader()); + } catch (Exception ignored) { + // Should not fail, but it does not harm to fail. + } + } + @SuppressWarnings("UnusedCatchParameter") private static InternalLoggerFactory newDefaultFactory(String name) { InternalLoggerFactory f; diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java index 14991a30d9..1845bfaca0 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannel.java @@ -50,7 +50,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha private MessageSizeEstimator.Handle estimatorHandle; private final Channel parent; - private final ChannelId id = DefaultChannelId.newInstance(); + private final ChannelId id; private final Unsafe unsafe; private final DefaultChannelPipeline pipeline; private final ChannelFuture succeededFuture = new SucceededChannelFuture(this, null); @@ -75,6 +75,20 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha */ protected AbstractChannel(Channel parent) { this.parent = parent; + id = DefaultChannelId.newInstance(); + unsafe = newUnsafe(); + pipeline = new DefaultChannelPipeline(this); + } + + /** + * Creates a new instance. + * + * @param parent + * the parent of this channel. {@code null} if there's no parent. + */ + protected AbstractChannel(Channel parent, ChannelId id) { + this.parent = parent; + this.id = id; unsafe = newUnsafe(); pipeline = new DefaultChannelPipeline(this); } diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelId.java b/transport/src/main/java/io/netty/channel/DefaultChannelId.java index 1210c92f5c..fb0e67b042 100644 --- a/transport/src/main/java/io/netty/channel/DefaultChannelId.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelId.java @@ -65,26 +65,6 @@ final class DefaultChannelId implements ChannelId { } static { - byte[] machineId = null; - String customMachineId = SystemPropertyUtil.get("io.netty.machineId"); - if (customMachineId != null) { - if (MACHINE_ID_PATTERN.matcher(customMachineId).matches()) { - machineId = parseMachineId(customMachineId); - logger.debug("-Dio.netty.machineId: {} (user-set)", customMachineId); - } else { - logger.warn("-Dio.netty.machineId: {} (malformed)", customMachineId); - } - } - - if (machineId == null) { - machineId = defaultMachineId(); - if (logger.isDebugEnabled()) { - logger.debug("-Dio.netty.machineId: {} (auto-detected)", formatAddress(machineId)); - } - } - - MACHINE_ID = machineId; - int processId = -1; String customProcessId = SystemPropertyUtil.get("io.netty.processId"); if (customProcessId != null) { @@ -110,6 +90,26 @@ final class DefaultChannelId implements ChannelId { } PROCESS_ID = processId; + + byte[] machineId = null; + String customMachineId = SystemPropertyUtil.get("io.netty.machineId"); + if (customMachineId != null) { + if (MACHINE_ID_PATTERN.matcher(customMachineId).matches()) { + machineId = parseMachineId(customMachineId); + logger.debug("-Dio.netty.machineId: {} (user-set)", customMachineId); + } else { + logger.warn("-Dio.netty.machineId: {} (malformed)", customMachineId); + } + } + + if (machineId == null) { + machineId = defaultMachineId(); + if (logger.isDebugEnabled()) { + logger.debug("-Dio.netty.machineId: {} (auto-detected)", formatAddress(machineId)); + } + } + + MACHINE_ID = machineId; } @SuppressWarnings("DynamicRegexReplaceableByCompiledPattern") diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java index 167946b7c2..81683d3796 100644 --- a/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java +++ b/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java @@ -74,7 +74,7 @@ public class EmbeddedChannel extends AbstractChannel { * @param handlers the @link ChannelHandler}s which will be add in the {@link ChannelPipeline} */ public EmbeddedChannel(ChannelHandler... handlers) { - super(null); + super(null, EmbeddedChannelId.INSTANCE); if (handlers == null) { throw new NullPointerException("handlers"); diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannelId.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannelId.java new file mode 100644 index 0000000000..01675c2f68 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannelId.java @@ -0,0 +1,65 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.channel.embedded; + +import io.netty.channel.ChannelId; + +/** + * A dummy {@link ChannelId} implementation. + */ +final class EmbeddedChannelId implements ChannelId { + + private static final long serialVersionUID = -251711922203466130L; + + static final ChannelId INSTANCE = new EmbeddedChannelId(); + + private EmbeddedChannelId() { } + + @Override + public String asShortText() { + return toString(); + } + + @Override + public String asLongText() { + return toString(); + } + + @Override + public int compareTo(ChannelId o) { + if (o == INSTANCE) { + return 0; + } + + return asLongText().compareTo(o.asLongText()); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return "embedded"; + } +}