static void close(NioSocketChannel channel, ChannelFuture future) { NioWorker worker = channel.worker; boolean connected = channel.isConnected(); boolean bound = channel.isBound(); try { channel.socket.close(); worker.cancelledKeys++; if (channel.setClosed()) { future.setSuccess(); if (connected) { fireChannelDisconnected(channel); } if (bound) { fireChannelUnbound(channel); } cleanUpWriteBuffer(channel); fireChannelClosed(channel); } else { future.setSuccess(); } } catch (Throwable t) { future.setFailure(t); fireExceptionCaught(channel, t); } }
private static void clearOpWrite(NioSocketChannel channel) { NioWorker worker = channel.worker; Selector selector = worker.selector; SelectionKey key = channel.socket.keyFor(selector); if (key == null) { return; } if (!key.isValid()) { close(key); return; } int interestOps; boolean changed = false; // interestOps can change at any time and at any thread. // Acquire a lock to avoid possible race condition. synchronized (channel.interestOpsLock) { interestOps = channel.getRawInterestOps(); if ((interestOps & SelectionKey.OP_WRITE) != 0) { interestOps &= ~SelectionKey.OP_WRITE; key.interestOps(interestOps); changed = true; } } if (changed) { channel.setRawInterestOpsNow(interestOps); } }
@Override protected boolean read(SelectionKey k) { final SocketChannel ch = (SocketChannel) k.channel(); final NioSocketChannel channel = (NioSocketChannel) k.attachment(); final ReceiveBufferSizePredictor predictor = channel.getConfig().getReceiveBufferSizePredictor(); final int predictedRecvBufSize = predictor.nextReceiveBufferSize(); int ret = 0; int readBytes = 0; boolean failure = true; ByteBuffer bb = recvBufferPool.acquire(predictedRecvBufSize); try { while ((ret = ch.read(bb)) > 0) { readBytes += ret; if (!bb.hasRemaining()) { break; } } failure = false; } catch (ClosedChannelException e) { // Can happen, and does not need a user attention. } catch (Throwable t) { fireExceptionCaught(channel, t); } if (readBytes > 0) { bb.flip(); final ChannelBufferFactory bufferFactory = channel.getConfig().getBufferFactory(); final ChannelBuffer buffer = bufferFactory.getBuffer(readBytes); buffer.setBytes(0, bb); buffer.writerIndex(readBytes); recvBufferPool.release(bb); // Update the predictor. predictor.previousReceiveBufferSize(readBytes); // Fire the event. fireMessageReceived(channel, buffer); } else { recvBufferPool.release(bb); } if (ret < 0 || failure) { k.cancel(); // Some JDK implementations run into an infinite loop without this. close(channel, succeededFuture(channel)); return false; } return true; }
private static void cleanUpWriteBuffer(NioSocketChannel channel) { Exception cause = null; boolean fireExceptionCaught = false; // Clean up the stale messages in the write buffer. synchronized (channel.writeLock) { MessageEvent evt = channel.currentWriteEvent; if (evt != null) { channel.currentWriteEvent = null; channel.currentWriteIndex = 0; // Create the exception only once to avoid the excessive overhead // caused by fillStackTrace. if (channel.isOpen()) { cause = new NotYetConnectedException(); } else { cause = new ClosedChannelException(); } evt.getFuture().setFailure(cause); fireExceptionCaught = true; } Queue<MessageEvent> writeBuffer = channel.writeBuffer; if (!writeBuffer.isEmpty()) { // Create the exception only once to avoid the excessive overhead // caused by fillStackTrace. if (cause == null) { if (channel.isOpen()) { cause = new NotYetConnectedException(); } else { cause = new ClosedChannelException(); } } for (; ; ) { evt = writeBuffer.poll(); if (evt == null) { break; } evt.getFuture().setFailure(cause); fireExceptionCaught = true; } } } if (fireExceptionCaught) { fireExceptionCaught(channel, cause); } }
static void write(final NioSocketChannel channel, boolean mightNeedWakeup) { if (!channel.isConnected()) { cleanUpWriteBuffer(channel); return; } if (mightNeedWakeup && scheduleWriteIfNecessary(channel)) { return; } if (channel.inWriteNowLoop) { scheduleWriteIfNecessary(channel); } else { writeNow(channel, channel.getConfig().getWriteSpinCount()); } }
private static boolean read(SelectionKey k) { ScatteringByteChannel ch = (ScatteringByteChannel) k.channel(); NioSocketChannel channel = (NioSocketChannel) k.attachment(); ReceiveBufferSizePredictor predictor = channel.getConfig().getReceiveBufferSizePredictor(); ChannelBufferFactory bufferFactory = channel.getConfig().getBufferFactory(); ChannelBuffer buffer = bufferFactory.getBuffer(predictor.nextReceiveBufferSize()); int ret = 0; int readBytes = 0; boolean failure = true; try { while ((ret = buffer.writeBytes(ch, buffer.writableBytes())) > 0) { readBytes += ret; if (!buffer.writable()) { break; } } failure = false; } catch (AsynchronousCloseException e) { // Can happen, and does not need a user attention. } catch (Throwable t) { fireExceptionCaught(channel, t); } if (readBytes > 0) { // Update the predictor. predictor.previousReceiveBufferSize(readBytes); // Fire the event. fireMessageReceived(channel, buffer); } if (ret < 0 || failure) { close(k); return false; } return true; }
public void run() { SocketAddress localAddress = channel.getLocalAddress(); SocketAddress remoteAddress = channel.getRemoteAddress(); if (localAddress == null || remoteAddress == null) { if (future != null) { future.setFailure(new ClosedChannelException()); } close(channel, succeededFuture(channel)); return; } try { if (server) { channel.channel.configureBlocking(false); } synchronized (channel.interestOpsLock) { channel.channel.register(selector, channel.getRawInterestOps(), channel); } if (future != null) { channel.setConnected(); future.setSuccess(); } if (server || !((NioClientSocketChannel) channel).boundManually) { fireChannelBound(channel, localAddress); } fireChannelConnected(channel, remoteAddress); } catch (IOException e) { if (future != null) { future.setFailure(e); } close(channel, succeededFuture(channel)); if (!(e instanceof ClosedChannelException)) { throw new ChannelException("Failed to register a socket to the selector.", e); } } }
static void setInterestOps(NioSocketChannel channel, ChannelFuture future, int interestOps) { boolean changed = false; try { // interestOps can change at any time and at any thread. // Acquire a lock to avoid possible race condition. synchronized (channel.interestOpsLock) { NioWorker worker = channel.worker; Selector selector = worker.selector; SelectionKey key = channel.socket.keyFor(selector); if (key == null || selector == null) { // Not registered to the worker yet. // Set the rawInterestOps immediately; RegisterTask will pick it up. channel.setRawInterestOpsNow(interestOps); return; } // Override OP_WRITE flag - a user cannot change this flag. interestOps &= ~Channel.OP_WRITE; interestOps |= channel.getRawInterestOps() & Channel.OP_WRITE; switch (CONSTRAINT_LEVEL) { case 0: if (channel.getRawInterestOps() != interestOps) { key.interestOps(interestOps); if (Thread.currentThread() != worker.thread && worker.wakenUp.compareAndSet(false, true)) { selector.wakeup(); } changed = true; } break; case 1: case 2: if (channel.getRawInterestOps() != interestOps) { if (Thread.currentThread() == worker.thread) { key.interestOps(interestOps); changed = true; } else { worker.selectorGuard.readLock().lock(); try { if (worker.wakenUp.compareAndSet(false, true)) { selector.wakeup(); } key.interestOps(interestOps); changed = true; } finally { worker.selectorGuard.readLock().unlock(); } } } break; default: throw new Error(); } } future.setSuccess(); if (changed) { channel.setRawInterestOpsNow(interestOps); fireChannelInterestChanged(channel); } } catch (CancelledKeyException e) { // setInterestOps() was called on a closed channel. ClosedChannelException cce = new ClosedChannelException(); future.setFailure(cce); fireExceptionCaught(channel, cce); } catch (Throwable t) { future.setFailure(t); fireExceptionCaught(channel, t); } }
private static void writeNow(NioSocketChannel channel, int writeSpinCount) { boolean open = true; boolean addOpWrite = false; boolean removeOpWrite = false; MessageEvent evt; ChannelBuffer buf; int bufIdx; int writtenBytes = 0; Queue<MessageEvent> writeBuffer = channel.writeBuffer; synchronized (channel.writeLock) { channel.inWriteNowLoop = true; evt = channel.currentWriteEvent; for (; ; ) { if (evt == null) { evt = writeBuffer.poll(); if (evt == null) { channel.currentWriteEvent = null; removeOpWrite = true; break; } evt = consolidateComposite(evt); buf = (ChannelBuffer) evt.getMessage(); bufIdx = buf.readerIndex(); } else { buf = (ChannelBuffer) evt.getMessage(); bufIdx = channel.currentWriteIndex; } try { for (int i = writeSpinCount; i > 0; i--) { int localWrittenBytes = buf.getBytes(bufIdx, channel.socket, buf.writerIndex() - bufIdx); if (localWrittenBytes != 0) { bufIdx += localWrittenBytes; writtenBytes += localWrittenBytes; break; } } if (bufIdx == buf.writerIndex()) { // Successful write - proceed to the next message. channel.currentWriteEvent = null; evt.getFuture().setSuccess(); evt = null; } else { // Not written fully - perhaps the kernel buffer is full. channel.currentWriteEvent = evt; channel.currentWriteIndex = bufIdx; addOpWrite = true; break; } } catch (AsynchronousCloseException e) { // Doesn't need a user attention - ignore. channel.currentWriteEvent = evt; channel.currentWriteIndex = bufIdx; } catch (Throwable t) { channel.currentWriteEvent = null; evt.getFuture().setFailure(t); evt = null; fireExceptionCaught(channel, t); if (t instanceof IOException) { open = false; close(channel, succeededFuture(channel)); } } } channel.inWriteNowLoop = false; } fireWriteComplete(channel, writtenBytes); if (open) { if (addOpWrite) { setOpWrite(channel); } else if (removeOpWrite) { clearOpWrite(channel); } } }