@Override public void run() { try { channel.channel.register(boss.selector, SelectionKey.OP_CONNECT, channel); } catch (ClosedChannelException e) { channel.worker.close(channel, succeededFuture(channel)); } int connectTimeout = channel.getConfig().getConnectTimeoutMillis(); if (connectTimeout > 0) { channel.connectDeadlineNanos = System.nanoTime() + connectTimeout * 1000000L; } }
@Override public void run() { boolean shutdown = false; Selector selector = this.selector; long lastConnectTimeoutCheckTimeNanos = System.nanoTime(); for (; ; ) { wakenUp.set(false); try { int selectedKeyCount = selector.select(10); // '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(); } processRegisterTaskQueue(); if (selectedKeyCount > 0) { processSelectedKeys(selector.selectedKeys()); } // Handle connection timeout every 10 milliseconds approximately. long currentTimeNanos = System.nanoTime(); if (currentTimeNanos - lastConnectTimeoutCheckTimeNanos >= 10 * 1000000L) { lastConnectTimeoutCheckTimeNanos = currentTimeNanos; processConnectTimeout(selector.keys(), currentTimeNanos); } // 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 // connection attempts are made in a one-by-one manner // instead of concurrent manner. if (selector.keys().isEmpty()) { if (shutdown || bossExecutor instanceof ExecutorService && ((ExecutorService) bossExecutor).isShutdown()) { synchronized (startStopLock) { if (registerTaskQueue.isEmpty() && selector.keys().isEmpty()) { started = false; try { selector.close(); } catch (IOException e) { if (logger.isWarnEnabled()) { 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) { if (logger.isWarnEnabled()) { logger.warn("Unexpected exception in the selector loop.", t); } // Prevent possible consecutive immediate failures. try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore. } } } }