package org.jboss.netty.channel.socket.nio; import org.jboss.netty.channel.socket.Worker; import org.jboss.netty.logging.InternalLogger; import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.util.ExternalResourceReleasable; import org.jboss.netty.util.internal.ExecutorUtil; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Abstract base class for {@link WorkerPool} implementations that create the {@link Worker}'s * up-front and return them in a "fair" fashion when calling {@link #nextWorker()} */ public abstract class AbstractNioWorkerPool<E extends AbstractNioWorker> implements WorkerPool<E>, ExternalResourceReleasable { /** * The worker pool raises an exception unless all worker threads start and run within this timeout (in seconds.) */ private static final int INITIALIZATION_TIMEOUT = 10; private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractNioWorkerPool.class); private final AbstractNioWorker[] workers; private final AtomicInteger workerIndex = new AtomicInteger(); private final Executor workerExecutor; private volatile boolean initialized; /** * Create a new instance * * @param workerExecutor the {@link Executor} to use for the {@link Worker}'s * @param workerCount the count of {@link Worker}'s to create */ AbstractNioWorkerPool(Executor workerExecutor, int workerCount) { this(workerExecutor, workerCount, true); } AbstractNioWorkerPool(Executor workerExecutor, int workerCount, boolean autoInit) { if (workerExecutor == null) { throw new NullPointerException("workerExecutor"); } if (workerCount <= 0) { throw new IllegalArgumentException( "workerCount (" + workerCount + ") " + "must be a positive integer."); } workers = new AbstractNioWorker[workerCount]; this.workerExecutor = workerExecutor; if (autoInit) { init(); } } protected void init() { if (initialized) { throw new IllegalStateException("initialized already"); } initialized = true; for (int i = 0; i < workers.length; i++) { workers[i] = newWorker(workerExecutor); } waitForWorkerThreads(); } private void waitForWorkerThreads() { long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(INITIALIZATION_TIMEOUT); boolean warn = false; for (AbstractNioSelector worker: workers) { long waitTime = deadline - System.nanoTime(); try { if (waitTime <= 0) { if (worker.thread == null) { warn = true; break; } } else if (!worker.startupLatch.await(waitTime, TimeUnit.NANOSECONDS)) { warn = true; break; } } catch (InterruptedException ignore) { // Stop waiting for the worker threads and let someone else take care of the interruption. Thread.currentThread().interrupt(); break; } } if (warn) { logger.warn( "Failed to get all worker threads ready within " + INITIALIZATION_TIMEOUT + " second(s). " + "Make sure to specify the executor which has more threads than the requested workerCount. " + "If unsure, use Executors.newCachedThreadPool()."); } } /** * Only here for backward compability and will be removed in later releases. Please use * {@link #newWorker(Executor)} * * * @param executor the {@link Executor} to use * @return worker the new {@link Worker} * @deprecated use {@link #newWorker(Executor)} */ @Deprecated protected E createWorker(Executor executor) { throw new IllegalStateException("This will be removed. Override this and the newWorker(..) method!"); } /** * Create a new {@link Worker} which uses the given {@link Executor} to service IO. * * This method will be made abstract in further releases (once {@link #createWorker(Executor)} * was removed). * * * @param executor the {@link Executor} to use * @return worker the new {@link Worker} */ @SuppressWarnings("deprecation") protected E newWorker(Executor executor) { return createWorker(executor); } @SuppressWarnings("unchecked") public E nextWorker() { return (E) workers[Math.abs(workerIndex.getAndIncrement() % workers.length)]; } public void rebuildSelectors() { for (AbstractNioWorker worker: workers) { worker.rebuildSelector(); } } public void releaseExternalResources() { shutdown(); ExecutorUtil.shutdownNow(workerExecutor); } public void shutdown() { for (AbstractNioWorker worker: workers) { worker.shutdown(); } } }