Пример #1
0
    /** 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);
          }
        }
      }
    }
Пример #2
0
  /**
   * 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");
    }
  }