package org.jboss.netty.channel.socket.nio; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelException; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.logging.InternalLogger; import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.util.ThreadNameDeterminer; import org.jboss.netty.util.ThreadRenamingRunnable; import org.jboss.netty.util.internal.DeadLockProofWorker; import java.io.IOException; import java.nio.channels.CancelledKeyException; import java.nio.channels.DatagramChannel; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.ConcurrentModificationException; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; abstract class AbstractNioSelector implements NioSelector { private static final AtomicInteger nextId = new AtomicInteger(); //ԭ�Ӳ��� private final int id = nextId.incrementAndGet(); /** * Internal Netty logger. */ protected static final InternalLogger logger = InternalLoggerFactory .getInstance(AbstractNioSelector.class); private static final int CLEANUP_INTERVAL = 256; // XXX Hard-coded value, but won't need customization. /** * ����������ִ��������ִ������Runnables������ͨ��ע������ */ private final Executor executor; /** * ���worker��������ô�ñ�����ָ��������Thread����Ϊ��Щ��������ݸñ��������ж��� * If this worker has been started thread will be a reference to the thread * used when starting. i.e. the current thread when the run method is executed. */ protected volatile Thread thread; /** * ������ֱ��IO�߳����������ҽ�����thread��Ϊ��Чֵ�� * Count down to 0 when the I/O thread starts and {@link #thread} is set to non-null. */ final CountDownLatch startupLatch = new CountDownLatch(1); /** * ���ij�Ա�� * NIO���е�ѡ����Selector */ protected volatile Selector selector; /** * ���� Selector.select ����������״̬������ԭ�ӱ����� * Boolean that controls determines if a blocked Selector.select should * break out of its selection process. In our case we use a timeone for * the select method and the select method will block for that time unless * waken up. */ protected final AtomicBoolean wakenUp = new AtomicBoolean(); /** * ���ǵĹ������С� */ private final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<Runnable>(); private volatile int cancelledKeys; // should use AtomicInteger but we just need approximation /** * �������ȵ��̹߳رա� */ private final CountDownLatch shutdownLatch = new CountDownLatch(1); private volatile boolean shutdown; AbstractNioSelector(Executor executor) { this(executor, null); } AbstractNioSelector(Executor executor, ThreadNameDeterminer determiner) { this.executor = executor; // ������� AbstractNioSelector�� openSelector(determiner); } /** * �����ChannelFuture�����Channel��Ͻ�Ĺ��������С� */ public void register(Channel channel, ChannelFuture future) { //����һ��ע�����񣨾������ϲ�ʵ�֣���������빤�����С� Runnable task = createRegisterTask(channel, future); registerTask(task); } protected final void registerTask(Runnable task) { taskQueue.add(task); Selector selector = this.selector; if (selector != null) { //Causes the first selection operation that has not yet returned to return immediately if (wakenUp.compareAndSet(false, true)) { selector.wakeup(); } } else { if (taskQueue.remove(task)) { // the selector was null this means the Worker has already been shutdown. throw new RejectedExecutionException("Worker has already been shutdown"); } } } protected final boolean isIoThread() { return Thread.currentThread() == thread; } public void rebuildSelector() { // ������������߳��е���rebuildSelector����ô�ͽ�����Ϊһ��������뵽�������С� if (!isIoThread()) { taskQueue.add(new Runnable() { public void run() { rebuildSelector(); } }); return; } final Selector oldSelector = selector; final Selector newSelector; if (oldSelector == null) { return; } try { // �޷��ٴδ�һ��Selector�� newSelector = SelectorUtil.open(); } catch (Exception e) { logger.warn("Failed to create a new Selector.", e); return; } //Ȼ������е�ChannelǨ�Ƶ�����µ�Selector�ϡ� // Register all channels to the new Selector. int nChannels = 0; for (;;) { try { for (SelectionKey key: oldSelector.keys()) { try { if (key.channel().keyFor(newSelector) != null) { continue; } int interestOps = key.interestOps(); key.cancel(); key.channel().register(newSelector, interestOps, key.attachment()); nChannels ++; } catch (Exception e) { logger.warn("Failed to re-register a Channel to the new Selector,", e); close(key); } } } catch (ConcurrentModificationException e) { // Probably due to concurrent modification of the key set. continue; } break; } // ���³ɹ��� selector = newSelector; try { // time to close the old selector as everything else is registered to the new one oldSelector.close(); } catch (Throwable t) { if (logger.isWarnEnabled()) { logger.warn("Failed to close the old Selector.", t); } } logger.info("Migrated " + nChannels + " channel(s) to the new Selector,"); } public void run() { thread = Thread.currentThread(); // �򿪱����� startupLatch.countDown(); int selectReturnsImmediately = 0; Selector selector = this.selector; if (selector == null) { return; } // use 80% of the timeout for measure final long minSelectTimeout = SelectorUtil.SELECT_TIMEOUT_NANOS * 80 / 100; boolean wakenupFromLoop = false; for (;;) { wakenUp.set(false); try { long beforeSelect = System.nanoTime(); // �����ж��ٸ�Channel׼������ int selected = select(selector); if (SelectorUtil.EPOLL_BUG_WORKAROUND && selected == 0 && !wakenupFromLoop && !wakenUp.get()) { //����select������ʱ�䣻 long timeBlocked = System.nanoTime() - beforeSelect; //���С����С��ʱʱ�����ƣ� if (timeBlocked < minSelectTimeout) { boolean notConnected = false; // loop over all keys as the selector may was unblocked because of a closed channel for (SelectionKey key: selector.keys()) { SelectableChannel ch = key.channel(); try { if (ch instanceof DatagramChannel && !ch.isOpen() || ch instanceof SocketChannel && !((SocketChannel) ch).isConnected()) { notConnected = true; // cancel the key just to be on the safe side key.cancel(); } } catch (CancelledKeyException e) { // ignore } } if (notConnected) { selectReturnsImmediately = 0; } else { //�ڳ�ʱ����֮ǰ�ͷ��أ����ҷ��صĽ����0��������ǵ���jdk epoll bug��ԭ���ۻ��� selectReturnsImmediately ++; } } else { // �dz�ʱ�� selectReturnsImmediately = 0; } // ����jdk epoll bug��������Ҫ�滻�����Selector������ //Ȼ��������һ�ֵ�select���� if (selectReturnsImmediately == 1024) { // The selector returned immediately for 10 times in a row, // so recreate one selector as it seems like we hit the // famous epoll(..) jdk bug. rebuildSelector(); selector = this.selector; selectReturnsImmediately = 0; wakenupFromLoop = false; // try to select again continue; } } else { // reset counter selectReturnsImmediately = 0; } /** * �ڵ���selector.wakeup()֮ǰ������ִ��wakenUp.compareAndSet(false, true)�� * ����Сwake-up�Ŀ�������ΪSelector.wakeup()ִ�еĴ��ۺܴ� * Ȼ�����ַ�������һ�־�̬����������������� wakenUp ����Ϊtrue̫���ʱ�� * 1��Selecttor��'wakenUp.set(false)'��'selector.select(...)'֮��������BAD���� * 2����'selector.select(...)'��'if (wakenUp.get()) { ... }'����ʱOK�ġ� * �ڵ�һ������£�'wakenUp'����Ϊ��true������û�ж��Ǹ�select��Ч�����������ý��������Ǹ� * 'selector.select(...)'����������ֱ������һ��ѭ������'wakenUp' ���ٴ���ΪFALSE��ʱ�� * ��ô 'wakenUp.compareAndSet(false, true)'�ͻ�ʧ�ܣ��κ��뾪��Selector�ij��Զ���ʧ�ܣ� * ���½�������'selector.select(...)'������ν�������� * * Ϊ�˽��������⣬����selector.select(...)֮���ж�wakenUp��true��ʱ����������һ�� * selector.wakeup()�� * �������������˵������selector�IJ������ǵ�Ч�ġ� */ if (wakenUp.get()) { wakenupFromLoop = true; selector.wakeup(); } else { wakenupFromLoop = false; } cancelledKeys = 0; processTaskQueue();// �������� selector = this.selector; // processTaskQueue() can call rebuildSelector() if (shutdown) { this.selector = null; // process one time again processTaskQueue(); for (SelectionKey k: selector.keys()) { close(k); } try { // Ҫ�ر�Selector�� selector.close(); } catch (IOException e) { logger.warn("Failed to close a selector.", e); } // ����������� shutdownLatch.countDown(); break; } else { //���ĵĹ��̣��о����NioSelector��ʵ�� process(selector); } } catch (Throwable t) { logger.warn( "Unexpected exception in the selector loop.", t); // Prevent possible consecutive immediate failures that lead to // excessive CPU consumption. try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore. } } } } /** * �������AbstractNioWorker��������Ӧ��Selector������������ע��AbstractNioChannel�� */ private void openSelector(ThreadNameDeterminer determiner) { try { // ����һ��Selector�� selector = SelectorUtil.open(); } catch (Throwable t) { throw new ChannelException("Failed to create a selector.", t); } // Start the worker thread with the new Selector. boolean success = false; try { //ִ����������Ӻζ����ɾ������ʵ�֡� DeadLockProofWorker.start(executor, newThreadRenamingRunnable(id, determiner)); success = true; } finally { if (!success) { // Release the Selector if the execution fails. try { // ���ʧ�ܵĻ��͹ر�Selector�� selector.close(); } catch (Throwable t) { logger.warn("Failed to close a selector.", t); } selector = null; // The method will return to the caller at this point. } } assert selector != null && selector.isOpen(); } private void processTaskQueue() { for (;;) { final Runnable task = taskQueue.poll(); if (task == null) { break; } task.run(); try { cleanUpCancelledKeys(); } catch (IOException e) { // Ignore } } } protected final void increaseCancelledKeys() { cancelledKeys ++; } protected final boolean cleanUpCancelledKeys() throws IOException { if (cancelledKeys >= CLEANUP_INTERVAL) { cancelledKeys = 0; // ������ Select�� selector.selectNow(); return true; } return false; } public void shutdown() { // ������IO�߳��е��� ���shutdown������ if (isIoThread()) { throw new IllegalStateException("Must not be called from a I/O-Thread to prevent deadlocks!"); } Selector selector = this.selector; shutdown = true; if (selector != null) { selector.wakeup(); } try { // �ȴ���������򿪡� shutdownLatch.await(); } catch (InterruptedException e) { logger.error("Interrupted while wait for resources to be released #" + id); //����Ҫ��ԭ�򣬱����ж�״̬�����ϲ��߳�ȥ���� Thread.currentThread().interrupt(); } } protected abstract void process(Selector selector) throws IOException; // ��selector.select()�����ķ�װ�� protected int select(Selector selector) throws IOException { return SelectorUtil.select(selector); } protected abstract void close(SelectionKey k); protected abstract ThreadRenamingRunnable newThreadRenamingRunnable(int id, ThreadNameDeterminer determiner); protected abstract Runnable createRegisterTask(Channel channel, ChannelFuture future); }