Example #1
0
 static void close(OioDatagramChannel channel, ChannelFuture future) {
   boolean connected = channel.isConnected();
   boolean bound = channel.isBound();
   try {
     channel.socket.close();
     if (channel.setClosed()) {
       future.setSuccess();
       if (connected) {
         // Notify the worker so it stops reading.
         Thread currentThread = Thread.currentThread();
         Thread workerThread = channel.workerThread;
         if (workerThread != null && currentThread != workerThread) {
           workerThread.interrupt();
         }
         fireChannelDisconnected(channel);
       }
       if (bound) {
         fireChannelUnbound(channel);
       }
       fireChannelClosed(channel);
     } else {
       future.setSuccess();
     }
   } catch (Throwable t) {
     future.setFailure(t);
     fireExceptionCaught(channel, t);
   }
 }
  private void connect(
      OioDatagramChannel channel, ChannelFuture future, SocketAddress remoteAddress) {

    boolean bound = channel.isBound();
    boolean connected = false;
    boolean workerStarted = false;

    future.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);

    // Clear the cached address so that the next getRemoteAddress() call
    // updates the cache.
    channel.remoteAddress = null;

    try {
      channel.socket.connect(remoteAddress);
      connected = true;

      // Fire events.
      future.setSuccess();
      if (!bound) {
        fireChannelBound(channel, channel.getLocalAddress());
      }
      fireChannelConnected(channel, channel.getRemoteAddress());

      String threadName =
          "Old I/O datagram worker (channelId: "
              + channel.getId()
              + ", "
              + channel.getLocalAddress()
              + " => "
              + channel.getRemoteAddress()
              + ')';
      if (!bound) {
        // Start the business.
        workerExecutor.execute(
            new IoWorkerRunnable(
                new ThreadRenamingRunnable(new OioDatagramWorker(channel), threadName)));
      } else {
        // Worker started by bind() - just rename.
        Thread workerThread = channel.workerThread;
        if (workerThread != null) {
          try {
            workerThread.setName(threadName);
          } catch (SecurityException e) {
            // Ignore.
          }
        }
      }

      workerStarted = true;
    } catch (Throwable t) {
      future.setFailure(t);
      fireExceptionCaught(channel, t);
    } finally {
      if (connected && !workerStarted) {
        OioDatagramWorker.close(channel, future);
      }
    }
  }
Example #3
0
  @Override
  protected boolean scheduleWriteIfNecessary(final AbstractNioChannel<?> channel) {
    final Thread currentThread = Thread.currentThread();
    final Thread workerThread = thread;
    if (currentThread != workerThread) {
      if (channel.writeTaskInTaskQueue.compareAndSet(false, true)) {
        boolean offered = writeTaskQueue.offer(channel.writeTask);
        assert offered;
      }

      if (!(channel instanceof NioAcceptedSocketChannel)
          || ((NioAcceptedSocketChannel) channel).bossThread != currentThread) {
        final Selector workerSelector = selector;
        if (workerSelector != null) {
          if (wakenUp.compareAndSet(false, true)) {
            workerSelector.wakeup();
          }
        }
      } else {
        // A write request can be made from an acceptor thread (boss)
        // when a user attempted to write something in:
        //
        //   * channelOpen()
        //   * channelBound()
        //   * channelConnected().
        //
        // In this case, there's no need to wake up the selector because
        // the channel is not even registered yet at this moment.
      }

      return true;
    }

    return false;
  }
Example #4
0
  static void setInterestOps(OioDatagramChannel channel, ChannelFuture future, int interestOps) {

    // 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();
          }
        }

        fireChannelInterestChanged(channel);
      }
    } catch (Throwable t) {
      future.setFailure(t);
      fireExceptionCaught(channel, t);
    }
  }
Example #5
0
  public void run() {
    channel.workerThread = Thread.currentThread();
    final MulticastSocket socket = channel.socket;

    while (channel.isOpen()) {
      synchronized (channel.interestOpsLock) {
        while (!channel.isReadable()) {
          try {
            // notify() is not called at all.
            // close() and setInterestOps() calls Thread.interrupt()
            channel.interestOpsLock.wait();
          } catch (InterruptedException e) {
            if (!channel.isOpen()) {
              break;
            }
          }
        }
      }

      ReceiveBufferSizePredictor predictor = channel.getConfig().getReceiveBufferSizePredictor();

      byte[] buf = new byte[predictor.nextReceiveBufferSize()];
      DatagramPacket packet = new DatagramPacket(buf, buf.length);
      try {
        socket.receive(packet);
      } catch (InterruptedIOException e) {
        // Can happen on interruption.
        // Keep receiving unless the channel is closed.
        continue;
      } catch (Throwable t) {
        if (!channel.socket.isClosed()) {
          fireExceptionCaught(channel, t);
        }
        break;
      }

      fireMessageReceived(
          channel,
          channel.getConfig().getBufferFactory().getBuffer(buf, 0, packet.getLength()),
          packet.getSocketAddress());
    }

    // Setting the workerThread to null will prevent any channel
    // operations from interrupting this thread from now on.
    channel.workerThread = null;

    // Clean up.
    close(channel, succeededFuture(channel));
  }
Example #6
0
  static void disconnect(OioDatagramChannel channel, ChannelFuture future) {
    boolean connected = channel.isConnected();
    try {
      channel.socket.disconnect();
      future.setSuccess();
      if (connected) {
        // Update the worker's thread name to reflect the state change.
        Thread workerThread = channel.workerThread;
        if (workerThread != null) {
          try {
            workerThread.setName("Old I/O datagram worker (" + channel + ')');
          } catch (SecurityException e) {
            // Ignore.
          }
        }

        // Notify.
        fireChannelDisconnected(channel);
      }
    } catch (Throwable t) {
      future.setFailure(t);
      fireExceptionCaught(channel, t);
    }
  }
Example #7
0
  private ChannelBuffer unwrap(
      ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, int offset, int length)
      throws SSLException {
    ByteBuffer inNetBuf = buffer.toByteBuffer(offset, length);
    ByteBuffer outAppBuf = bufferPool.acquire();

    try {
      boolean needsWrap = false;
      loop:
      for (; ; ) {
        SSLEngineResult result;
        synchronized (handshakeLock) {
          if (!handshaken
              && !handshaking
              && !engine.getUseClientMode()
              && !engine.isInboundDone()
              && !engine.isOutboundDone()) {
            handshake();
          }

          try {
            result = engine.unwrap(inNetBuf, outAppBuf);
          } catch (SSLException e) {
            throw e;
          }

          final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
          handleRenegotiation(handshakeStatus);
          switch (handshakeStatus) {
            case NEED_UNWRAP:
              if (inNetBuf.hasRemaining() && !engine.isInboundDone()) {
                break;
              } else {
                break loop;
              }
            case NEED_WRAP:
              wrapNonAppData(ctx, channel);
              break;
            case NEED_TASK:
              runDelegatedTasks();
              break;
            case FINISHED:
              setHandshakeSuccess(channel);
              needsWrap = true;
              break loop;
            case NOT_HANDSHAKING:
              needsWrap = true;
              break loop;
            default:
              throw new IllegalStateException("Unknown handshake status: " + handshakeStatus);
          }
        }
      }

      if (needsWrap) {
        // wrap() acquires pendingUnencryptedWrites first and then
        // handshakeLock.  If handshakeLock is already hold by the
        // current thread, calling wrap() will lead to a dead lock
        // i.e. pendingUnencryptedWrites -> handshakeLock vs.
        //      handshakeLock -> pendingUnencryptedLock -> handshakeLock
        //
        // There is also a same issue between pendingEncryptedWrites
        // and pendingUnencryptedWrites.
        if (!Thread.holdsLock(handshakeLock)
            && !pendingEncryptedWritesLock.isHeldByCurrentThread()) {
          wrap(ctx, channel);
        }
      }

      outAppBuf.flip();

      if (outAppBuf.hasRemaining()) {
        ChannelBuffer frame = ChannelBuffers.buffer(outAppBuf.remaining());
        frame.writeBytes(outAppBuf.array(), 0, frame.capacity());
        return frame;
      } else {
        return null;
      }
    } catch (SSLException e) {
      setHandshakeFailure(channel, e);
      throw e;
    } finally {
      bufferPool.release(outAppBuf);
    }
  }
Example #8
0
  private ChannelFuture wrapNonAppData(ChannelHandlerContext ctx, Channel channel)
      throws SSLException {
    ChannelFuture future = null;
    ByteBuffer outNetBuf = bufferPool.acquire();

    SSLEngineResult result;
    try {
      for (; ; ) {
        synchronized (handshakeLock) {
          result = engine.wrap(EMPTY_BUFFER, outNetBuf);
        }

        if (result.bytesProduced() > 0) {
          outNetBuf.flip();
          ChannelBuffer msg = ChannelBuffers.buffer(outNetBuf.remaining());
          msg.writeBytes(outNetBuf.array(), 0, msg.capacity());
          outNetBuf.clear();

          future = future(channel);
          future.addListener(
              new ChannelFutureListener() {
                public void operationComplete(ChannelFuture future) throws Exception {
                  if (future.getCause() instanceof ClosedChannelException) {
                    synchronized (ignoreClosedChannelExceptionLock) {
                      ignoreClosedChannelException++;
                    }
                  }
                }
              });

          write(ctx, future, msg);
        }

        final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
        handleRenegotiation(handshakeStatus);
        switch (handshakeStatus) {
          case FINISHED:
            setHandshakeSuccess(channel);
            runDelegatedTasks();
            break;
          case NEED_TASK:
            runDelegatedTasks();
            break;
          case NEED_UNWRAP:
            if (!Thread.holdsLock(handshakeLock)) {
              // unwrap shouldn't be called when this method was
              // called by unwrap - unwrap will keep running after
              // this method returns.
              unwrap(ctx, channel, ChannelBuffers.EMPTY_BUFFER, 0, 0);
            }
            break;
          case NOT_HANDSHAKING:
          case NEED_WRAP:
            break;
          default:
            throw new IllegalStateException("Unexpected handshake status: " + handshakeStatus);
        }

        if (result.bytesProduced() == 0) {
          break;
        }
      }
    } catch (SSLException e) {
      setHandshakeFailure(channel, e);
      throw e;
    } finally {
      bufferPool.release(outNetBuf);
    }

    if (future == null) {
      future = succeededFuture(channel);
    }

    return future;
  }
Example #9
0
  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);
    }
  }
Example #10
0
  public void run() {
    thread = Thread.currentThread();

    boolean shutdown = false;
    Selector selector = this.selector;
    for (; ; ) {
      wakenUp.set(false);

      if (CONSTRAINT_LEVEL != 0) {
        selectorGuard.writeLock().lock();
        // This empty synchronization block prevents the selector
        // from acquiring its lock.
        selectorGuard.writeLock().unlock();
      }

      try {
        selector.select(500);

        // 'wakenUp.compareAndSet(false, true)' is always evaluated
        // before calling 'selector.wakeup()' to reduce the wake-up
        // overhead. (Selector.wakeup() is an expensive operation.)
        //
        // However, there is a race condition in this approach.
        // The race condition is triggered when 'wakenUp' is set to
        // true too early.
        //
        // 'wakenUp' is set to true too early if:
        // 1) Selector is waken up between 'wakenUp.set(false)' and
        //    'selector.select(...)'. (BAD)
        // 2) Selector is waken up between 'selector.select(...)' and
        //    'if (wakenUp.get()) { ... }'. (OK)
        //
        // In the first case, 'wakenUp' is set to true and the
        // following 'selector.select(...)' will wake up immediately.
        // Until 'wakenUp' is set to false again in the next round,
        // 'wakenUp.compareAndSet(false, true)' will fail, and therefore
        // any attempt to wake up the Selector will fail, too, causing
        // the following 'selector.select(...)' call to block
        // unnecessarily.
        //
        // To fix this problem, we wake up the selector again if wakenUp
        // is true immediately after selector.select(...).
        // It is inefficient in that it wakes up the selector for both
        // the first case (BAD - wake-up required) and the second case
        // (OK - no wake-up required).

        if (wakenUp.get()) {
          selector.wakeup();
        }

        cancelledKeys = 0;
        processRegisterTaskQueue();
        processWriteTaskQueue();
        processSelectedKeys(selector.selectedKeys());

        // Exit the loop when there's nothing to handle.
        // The shutdown flag is used to delay the shutdown of this
        // loop to avoid excessive Selector creation when
        // connections are registered in a one-by-one manner instead of
        // concurrent manner.
        if (selector.keys().isEmpty()) {
          if (shutdown
              || executor instanceof ExecutorService && ((ExecutorService) executor).isShutdown()) {

            synchronized (startStopLock) {
              if (registerTaskQueue.isEmpty() && selector.keys().isEmpty()) {
                started = false;
                try {
                  selector.close();
                } catch (IOException e) {
                  logger.warn("Failed to close a selector.", e);
                } finally {
                  this.selector = null;
                }
                break;
              } else {
                shutdown = false;
              }
            }
          } else {
            // Give one more second.
            shutdown = true;
          }
        } else {
          shutdown = false;
        }
      } catch (Throwable t) {
        logger.warn("Unexpected exception in the selector loop.", t);

        // Prevent possible consecutive immediate failures that lead to
        // excessive CPU consumption.
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          // Ignore.
        }
      }
    }
  }