package org.jboss.netty.channel.socket.oio; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.socket.Worker; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import static org.jboss.netty.channel.Channels.*; /** * OIOģ��������ַ�������ʵ�֣� * ÿ��Worker����������ӦChannel�ģ� * Abstract base class for Oio-Worker implementations * * @param <C> {@link AbstractOioChannel} */ abstract class AbstractOioWorker<C extends AbstractOioChannel> implements Worker { // ���е������������Ŷӣ� //û�����ù������еĴ�С��ע�⡣ private final Queue<Runnable> eventQueue = new ConcurrentLinkedQueue<Runnable>(); // ������Channel�� protected final C channel; /** * ��worker����֮���ø��ֶ������������̣߳� * 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; private volatile boolean done; protected AbstractOioWorker(C channel) { this.channel = channel; channel.worker = this; } public void run() { thread = channel.workerThread = Thread.currentThread(); while (channel.isOpen()) { synchronized (channel.interestOpsLock) { while (!channel.isReadable()) { try { // notify() is not called at all. // close() and setInterestOps() calls Thread.interrupt() //OIOģ���²��ɶ��͵ȴ� channel.interestOpsLock.wait(); } catch (InterruptedException e) { if (!channel.isOpen()) { break; } } } } boolean cont = false; try { //�����Ĵ����ȡ�������ݣ� cont = process(); } catch (Throwable t) { // ���Ƿ���socket��ʱ�쳣�� boolean readTimeout = t instanceof SocketTimeoutException; if (!readTimeout && !channel.isSocketClosed()) { fireExceptionCaught(channel, t); } if (readTimeout) { // the readTimeout was triggered because of the SO_TIMEOUT, // so just continue with the loop here cont = true; } } finally { // ����ͳһ��������е��¼��� processEventQueue(); } if (!cont) { break; } } //interestOpsLock�൱��Channel������ÿ�η������ֶΣ���Ҫ������ synchronized (channel.interestOpsLock) { // Setting the workerThread to null will prevent any channel // operations from interrupting this thread from now on. //Ҳ����˵�߳��Ѿ���ʼִ�У�������Channel�������ˡ� // // Do this while holding the lock to not race with close(...) or // setInterestOps(...) //����ľ��軹û����⣻ channel.workerThread = null; } // Clean up. close(channel, succeededFuture(channel), true); // Mark the worker event loop as done so we know that we need to run tasks directly and not queue them // See #287 done = true; // just to make we don't have something left processEventQueue(); } static boolean isIoThread(AbstractOioChannel channel) { return Thread.currentThread() == channel.workerThread; } public void executeInIoThread(Runnable task) { // check if the current thread is the worker thread // // Also check if the event loop of the worker is complete. If so we need to run the task now. // See #287 // Ҳ����˵�ֵ�����߳�ͨ�����У�����û���������������������ִ�С� if (Thread.currentThread() == thread || done) { task.run(); } else { // ������ӵ�������У� boolean added = eventQueue.offer(task); if (added) { // as we set the SO_TIMEOUT to 1 second this task will get picked up in 1 second at latest } } } private void processEventQueue() { for (;;) { final Runnable task = eventQueue.poll(); if (task == null) { break; } task.run(); } } /** * ������󷽷��Ǻ��ģ����Կ������˼�룬ÿ��һ����ΰ������ľ����꣬���� * ��Ȩ�������ϲ������ࣻ * ����������Ϣ�����Ҹ�����û�г����ʱ�����Channels.ireMessageReceived(Channel, Object) * * ����ֵ����ʾ���worker�Ƿ��������incoming messages�� */ abstract boolean process() throws IOException; static void setInterestOps( AbstractOioChannel channel, ChannelFuture future, int interestOps) { boolean iothread = isIoThread(channel); // Override OP_WRITE flag - a user cannot change this flag. interestOps &= ~Channel.OP_WRITE; interestOps |= channel.getInterestOps() & Channel.OP_WRITE; boolean changed = false; try { if (channel.getInterestOps() != interestOps) { if ((interestOps & Channel.OP_READ) != 0) { channel.setInterestOpsNow(Channel.OP_READ); } else { channel.setInterestOpsNow(Channel.OP_NONE); } changed = true; } future.setSuccess(); if (changed) { synchronized (channel.interestOpsLock) { channel.setInterestOpsNow(interestOps); // Notify the worker so it stops or continues reading. Thread currentThread = Thread.currentThread(); Thread workerThread = channel.workerThread; if (workerThread != null && currentThread != workerThread) { workerThread.interrupt(); } } if (iothread) { fireChannelInterestChanged(channel); } else { fireChannelInterestChangedLater(channel); } } } catch (Throwable t) { future.setFailure(t); if (iothread) { fireExceptionCaught(channel, t); } else { fireExceptionCaughtLater(channel, t); } } } static void close(AbstractOioChannel channel, ChannelFuture future) { close(channel, future, isIoThread(channel)); } private static void close(AbstractOioChannel channel, ChannelFuture future, boolean iothread) { boolean connected = channel.isConnected(); boolean bound = channel.isBound(); try { channel.closeSocket(); if (channel.setClosed()) { future.setSuccess(); if (connected) { Thread currentThread = Thread.currentThread(); synchronized (channel.interestOpsLock) { // We need to do this while hold the lock as otherwise // we may race and so interrupt the workerThread even // if we are in the workerThread now. // This can happen if run() set channel.workerThread to null // between workerThread != null and currentThread!= workerThread Thread workerThread = channel.workerThread; if (workerThread != null && currentThread != workerThread) { workerThread.interrupt(); } } if (iothread) { fireChannelDisconnected(channel); } else { fireChannelDisconnectedLater(channel); } } if (bound) { if (iothread) { fireChannelUnbound(channel); } else { fireChannelUnboundLater(channel); } } if (iothread) { fireChannelClosed(channel); } else { fireChannelClosedLater(channel); } } else { future.setSuccess(); } } catch (Throwable t) { future.setFailure(t); if (iothread) { fireExceptionCaught(channel, t); } else { fireExceptionCaughtLater(channel, t); } } } }