@Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) { final NioSocketChannel ch = (NioSocketChannel) e.getChannel(); NioWorker worker = ch.getWorker(); // Choose a handler final HandlerHolder handler = handlerManager.chooseHandler(worker); if (handler == null) { // Ignore return; } VertxInternal.instance.executeOnContext( handler.contextID, new Runnable() { public void run() { VertxInternal.instance.setContextID(handler.contextID); NetSocket sock = new NetSocket(ch, handler.contextID, Thread.currentThread()); socketMap.put(ch, sock); handler.handler.handle(sock); } }); }
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()); } }
@Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { final NioSocketChannel ch = (NioSocketChannel) e.getChannel(); final NetSocket sock = socketMap.get(ch); ch.close(); final Throwable t = e.getCause(); if (sock != null && t instanceof Exception) { VertxInternal.instance.executeOnContext( sock.getContextID(), new Runnable() { public void run() { sock.handleException((Exception) t); } }); } else { t.printStackTrace(); } }
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; }
@Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { final NioSocketChannel ch = (NioSocketChannel) e.getChannel(); final NetSocket sock = socketMap.remove(ch); ch.close(); final Throwable t = e.getCause(); log.error("Exception on netserver", t); if (sock != null && t instanceof Exception) { sock.getContext() .execute( new Runnable() { public void run() { sock.handleException((Exception) t); } }); } else { // Ignore - any exceptions not associated with any sock (e.g. failure in ssl handshake) will // be communicated explicitly } }
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); } } }
@Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) { final NioSocketChannel ch = (NioSocketChannel) e.getChannel(); NioWorker worker = ch.getWorker(); // Choose a handler final HandlerHolder handler = handlerManager.chooseHandler(worker); if (handler == null) { // Ignore return; } if (tcpHelper.isSSL()) { SslHandler sslHandler = (SslHandler) ch.getPipeline().get("ssl"); ChannelFuture fut = sslHandler.handshake(); fut.addListener( new ChannelFutureListener() { public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()) { connected(ch, handler); } else { log.error( "Client from origin " + ch.getRemoteAddress() + " failed to connect over ssl"); } } }); } else { connected(ch, handler); } }
/* Currently Netty does not provide all events for a connection on the same thread - e.g. connection open connection bound etc are provided on the acceptor thread. In vert.x we must ensure all events are executed on the correct event loop for the context This code will go away if Netty acts like a proper event loop. */ public void runOnCorrectThread(NioSocketChannel nch, Runnable runnable) { nch.getWorker().executeInIoThread(runnable, false); }
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); } } }