Esempio n. 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);
   }
 }
Esempio n. 2
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;
  }
Esempio n. 3
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));
  }
Esempio n. 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);
    }
  }
Esempio n. 5
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);
    }
  }
Esempio n. 6
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.
        }
      }
    }
  }