/** blocks until connected */ @Override SelectableChannel doConnect() throws IOException, InterruptedException { boolean success = false; final SocketChannel socketChannel = SocketChannel.open(); try { socketChannel.configureBlocking(false); socketChannel.socket().setReuseAddress(true); socketChannel.socket().setSoLinger(false, 0); socketChannel.socket().setSoTimeout(0); socketChannel.socket().setTcpNoDelay(true); try { socketChannel.connect(details.address()); } catch (UnresolvedAddressException e) { this.connectLater(); } // Under experiment, the concoction was found to be more successful if we // paused before registering the OP_CONNECT Thread.sleep(10); // the registration has be be run on the same thread as the selector addPendingRegistration( new Runnable() { @Override public void run() { final Attached attached = new Attached(); attached.connector = ClientConnector.this; try { socketChannel.register(selector, OP_CONNECT, attached); } catch (ClosedChannelException e) { if (socketChannel.isOpen()) LOG.error("", e); } } }); selector.wakeup(); success = true; return socketChannel; } finally { if (!success) { try { try { socketChannel.socket().close(); } catch (Exception e) { LOG.error("", e); } socketChannel.close(); } catch (IOException e) { LOG.error("", e); } } } }
/** * Attend to channels in the hub. * * <p>This method returns when {@link #close()} is called and the selector is shut down. */ public void run() { synchronized (startedLock) { started = true; selectorThread = Thread.currentThread(); startedLock.notifyAll(); } final String oldName = selectorThread.getName(); try { while (true) { try { while (true) { Callable<Void, IOException> t = selectorTasks.poll(); if (t == null) break; try { t.call(); } catch (IOException e) { LOGGER.log(WARNING, "Failed to process selectorTasks", e); // but keep on at the next task } } selectorThread.setName( "NioChannelHub keys=" + selector.keys().size() + " gen=" + (gen++) + ": " + oldName); selector.select(); } catch (IOException e) { whatKilledSelectorThread = e; LOGGER.log(WARNING, "Failed to select", e); abortAll(e); return; } Iterator<SelectionKey> itr = selector.selectedKeys().iterator(); while (itr.hasNext()) { SelectionKey key = itr.next(); itr.remove(); Object a = key.attachment(); if (a instanceof NioTransport) { final NioTransport t = (NioTransport) a; try { if (key.isReadable()) { if (t.rb.receive(t.rr()) == -1) { t.closeR(); } final byte[] buf = new byte[2]; // space for reading the chunk header int pos = 0; int packetSize = 0; while (true) { if (t.rb.peek(pos, buf) < buf.length) break; // we don't have enough to parse header int header = ChunkHeader.parse(buf); int chunk = ChunkHeader.length(header); pos += buf.length + chunk; packetSize += chunk; boolean last = ChunkHeader.isLast(header); if (last && pos <= t.rb.readable()) { // do we have the whole packet in our buffer? // read in the whole packet final byte[] packet = new byte[packetSize]; int r_ptr = 0; do { int r = t.rb.readNonBlocking(buf); assert r == buf.length; header = ChunkHeader.parse(buf); chunk = ChunkHeader.length(header); last = ChunkHeader.isLast(header); t.rb.readNonBlocking(packet, r_ptr, chunk); packetSize -= chunk; r_ptr += chunk; } while (!last); assert packetSize == 0; if (packet.length > 0) { t.swimLane.submit( new Runnable() { public void run() { t.receiver.handle(packet); } }); } pos = 0; } } if (t.rb.writable() == 0 && t.rb.readable() > 0) { String msg = "Command buffer overflow. Read " + t.rb.readable() + " bytes but still too small for a single command"; LOGGER.log(WARNING, msg); // to avoid infinite hang, abort this connection t.abort(new IOException(msg)); } if (t.rb.isClosed()) { // EOF. process this synchronously with respect to packets t.swimLane.submit( new Runnable() { public void run() { // if this EOF is unexpected, report an error. if (!t.getChannel().isInClosed()) t.getChannel().terminate(new EOFException()); } }); } } if (key.isValid() && key.isWritable()) { t.wb.send(t.ww()); if (t.wb.readable() < 0) { // done with sending all the data t.closeW(); } } t.reregister(); } catch (IOException e) { LOGGER.log(WARNING, "Communication problem", e); t.abort(e); } catch (CancelledKeyException e) { // see JENKINS-24050. I don't understand how this can happen, given that the selector // thread is the only thread that cancels keys. So to better understand what's going // on, // report the problem. LOGGER.log(SEVERE, "Unexpected key cancellation for " + t, e); // to be on the safe side, abort the communication. if we don't do this, it's possible // that the key never gets re-registered to the selector, and the traffic will hang // on this channel. t.abort(e); } } else { onSelected(key); } } } } catch (ClosedSelectorException e) { // end normally // TODO: what happens to all the registered ChannelPairs? don't we need to shut them down? whatKilledSelectorThread = e; } catch (RuntimeException e) { whatKilledSelectorThread = e; LOGGER.log(WARNING, "Unexpected shutdown of the selector thread", e); abortAll(e); throw e; } catch (Error e) { whatKilledSelectorThread = e; LOGGER.log(WARNING, "Unexpected shutdown of the selector thread", e); abortAll(e); throw e; } finally { selectorThread.setName(oldName); selectorThread = null; if (whatKilledSelectorThread == null) whatKilledSelectorThread = new AssertionError("NioChannelHub shouldn't exit normally"); } }