private void dispose(Selector localSelector, LinkedBlockingQueue localSelectorTasks) { Assert.eval(Thread.currentThread() == this); if (localSelector != null) { for (Object element : localSelector.keys()) { try { SelectionKey key = (SelectionKey) element; cleanupChannel(key.channel(), null); } catch (Exception e) { logger.warn("Exception trying to close channel", e); } } try { localSelector.close(); } catch (Exception e) { if ((Os.isMac()) && (Os.isUnix()) && (e.getMessage().equals("Bad file descriptor"))) { // I can't find a specific bug about this, but I also can't seem to prevent the // exception on the Mac. // So just logging this as warning. logger.warn("Exception trying to close selector: " + e.getMessage()); } else { logger.error("Exception trying to close selector", e); } } } }
@Override public void run() { selectorManager.notifyReady(); while (selectorManager.isStarted() && selector.isOpen()) { try { beforeSelect(); wakenUp.set(false); long before = -1; // Wether to look jvm bug if (isNeedLookingJVMBug()) { before = System.currentTimeMillis(); } long wait = DEFAULT_WAIT; if (nextTimeout > 0) { wait = nextTimeout; } int selected = selector.select(wait); if (selected == 0) { if (before != -1) { lookJVMBug(before, selected, wait); } selectTries++; // check tmeout and idle nextTimeout = checkSessionTimeout(); continue; } else { selectTries = 0; } } catch (ClosedSelectorException e) { break; } catch (IOException e) { log.error("Reactor select error", e); if (selector.isOpen()) { continue; } else { break; } } Set<SelectionKey> selectedKeys = selector.selectedKeys(); gate.lock(); try { postSelect(selectedKeys, selector.keys()); dispatchEvent(selectedKeys); } finally { gate.unlock(); } } if (selector != null) { if (selector.isOpen()) { try { controller.closeChannel(selector); selector.close(); } catch (IOException e) { controller.notifyException(e); log.error("stop reactor error", e); } } } }
/** Return the set of SelectionKey registered on this Selector. */ public Set<SelectionKey> keys() { if (selector != null) { return selector.keys(); } else { throw new IllegalStateException("Selector is not created!"); } }
void unregisterChannels(SelectionKeyRegistrationReference registrationReference) throws Exception { registrationReference.cancelRegistration(); SelectorIntraband defaultIntraband = (SelectorIntraband) registrationReference.getIntraband(); Selector selector = defaultIntraband.selector; Set<SelectionKey> keys = selector.keys(); while (!keys.isEmpty()) { selector.wakeup(); } SelectionKey readSelectionKey = registrationReference.readSelectionKey; SelectionKey writeSelectionKey = registrationReference.writeSelectionKey; SelectableChannel readSelectableChannel = readSelectionKey.channel(); SelectableChannel writeSelectableChannel = writeSelectionKey.channel(); while (readSelectableChannel.keyFor(selector) != null) ; while (writeSelectableChannel.keyFor(selector) != null) ; writeSelectableChannel.close(); readSelectableChannel.close(); }
public boolean processNow() throws IOException { int numKeys = selector.selectNow(); if (selector.keys().isEmpty()) return false; if (numKeys > 0) dispatchMessages(); return true; }
public void stop() { try { lock.lock(); if (networkJob != null && !networkJob.isCancelled()) { networkJob.cancel(true); networkJob = null; } try { if (selector != null) { selector.wakeup(); boolean isContinue = true; while (isContinue) { try { for (SelectionKey selectionKey : selector.keys()) { selectionKey.channel().close(); selectionKey.cancel(); } isContinue = false; // continue till all keys are cancelled } catch (ConcurrentModificationException e) { logger.warn( "An exception occurred while closing a selector key : '{}'", e.getMessage()); } } selector.close(); } } catch (IOException e) { logger.warn("An exception occurred while closing the selector : '{}'", e.getMessage()); } if (broadcastKey != null) { try { broadcastKey.channel().close(); } catch (IOException e) { logger.warn( "An exception occurred while closing the broadcast channel : '{}'", e.getMessage()); } } if (unicastKey != null) { try { unicastKey.channel().close(); } catch (IOException e) { logger.warn( "An exception occurred while closing the unicast channel : '{}'", e.getMessage()); } } ipAddress = null; } finally { lock.unlock(); } }
public void shutdown(final long period, final TimeUnit timeUnit) { channelDispatcher.stop(); for (SelectionKey selectionKey : socketChannelSelector.keys()) { final AbstractChannelReader reader = (AbstractChannelReader) selectionKey.attachment(); selectionKey.cancel(); if (reader != null) { while (!reader.isClosed()) { try { Thread.sleep(channelReaderFrequencyMSecs); } catch (InterruptedException e) { } } final ScheduledFuture<?> readerFuture = reader.getScheduledFuture(); readerFuture.cancel(false); } IOUtils.closeQuietly( selectionKey .channel()); // should already be closed via reader, but if reader did not exist... } IOUtils.closeQuietly(socketChannelSelector); for (SelectionKey selectionKey : serverSocketSelector.keys()) { selectionKey.cancel(); IOUtils.closeQuietly(selectionKey.channel()); } IOUtils.closeQuietly(serverSocketSelector); executor.shutdown(); try { executor.awaitTermination(period, timeUnit); } catch (final InterruptedException ex) { LOGGER.warn("Interrupted while trying to shutdown executor"); } final int currentBufferPoolSize = bufferPool.size(); final String warning = (currentBufferPoolSize != initialBufferPoolSize) ? "Initial buffer count=" + initialBufferPoolSize + " Current buffer count=" + currentBufferPoolSize + " Could indicate a buffer leak. Ensure all consumers are executed until they complete." : ""; LOGGER.info("Channel listener shutdown. " + warning); }
private void listen() throws IOException { // TODO Auto-generated method stub // 监听所有的事件 while (true) { selector.select(); // 返回值为本次触发的事件数 Set<SelectionKey> selectionKeys = selector.keys(); for (SelectionKey key : selectionKeys) { handle(key); } selectionKeys.clear(); // 清除处理过的事件 } }
/** MemcachedClient calls this method to handle IO over the connections. */ @SuppressWarnings("unchecked") public void handleIO() throws IOException { if (shutDown) { throw new IOException("No IO while shut down"); } // Deal with all of the stuff that's been added, but may not be marked // writable. handleInputQueue(); getLogger().debug("Done dealing with queue."); long delay = 0; if (!reconnectQueue.isEmpty()) { long now = System.currentTimeMillis(); long then = reconnectQueue.firstKey(); delay = Math.max(then - now, 1); } getLogger().debug("Selecting with delay of %sms", delay); assert selectorsMakeSense() : "Selectors don't make sense."; int selected = selector.select(delay); Set<SelectionKey> selectedKeys = selector.selectedKeys(); if (selectedKeys.isEmpty() && !shutDown) { getLogger().debug("No selectors ready, interrupted: " + Thread.interrupted()); if (++emptySelects > DOUBLE_CHECK_EMPTY) { for (SelectionKey sk : selector.keys()) { getLogger().info("%s has %s, interested in %s", sk, sk.readyOps(), sk.interestOps()); if (sk.readyOps() != 0) { getLogger().info("%s has a ready op, handling IO", sk); handleIO(sk); } else { queueReconnect((MemcachedNode) sk.attachment()); } } assert emptySelects < EXCESSIVE_EMPTY : "Too many empty selects"; } } else { getLogger().debug("Selected %d, selected %d keys", selected, selectedKeys.size()); emptySelects = 0; for (SelectionKey sk : selectedKeys) { handleIO(sk); } // for each selector selectedKeys.clear(); } if (!shutDown && !reconnectQueue.isEmpty()) { attemptReconnects(); } }
public void close() { selectable = false; selector.wakeup(); // forcibly close remaining sockets for (SelectionKey key : selector.keys()) { if (key != null) { safelyClose(key.channel()); } } safelyClose(selector); safelyClose(agent); safelyClose(channel); if (authSocket != null) { FileUtils.deleteQuietly(new File(authSocket)); } }
@Override public void update(Observable o, Object arg) { // ENVIAR POSICIONS // Si el que volem és actualitzar les posicions al joc if (arg instanceof int[]) { int[] lastPosition = (int[]) arg; enviar(lastPosition); } // ACABAR PARTIDA // Si el que volem és acabar la partida else if (arg instanceof Integer) { if ((int) arg == Const.ACABA_PARTIDA) { System.out.println( "~Partida Acabada | Desconectem " + arraySocketChannels.size() + " jugadors"); controlador.acaba(); enviar(Const.FINISH_CODE); System.out.print("Cancelació de les Claus..."); // CONCURRENCIA claus // Les claus son recursos compartits. Un dels problemes que ens podem trobar, // es que abans de que els clients rebin el FINISH_CODE, enviïn un paquet, // i quan es procesi la clau sigui cancelada. // CANCELEM LES CLAUS // Hem de cancelar només les claus que pertanyen a conexions amb // clients, que son les que retornen un SocketChannel quan invoquem // key.channel() synchronized (this) { for (SelectionKey key : selector.keys()) { if (key.channel() instanceof SocketChannel && arraySocketChannels.contains((SocketChannel) key.channel())) { key.cancel(); System.out.print(" | Clau cancelada"); } } } arraySocketChannels.clear(); System.out.println("\nArray de SocketChannels 'cleared'"); } } }
/** * Cleans up the contents of the selector, closing any server socket channels that might be * associated with it. Any connections that might have been established through those channels * should not be impacted. */ private void cleanUpSelector() { try { for (SelectionKey key : selector.keys()) { try { key.cancel(); } catch (Exception e) { logger.traceException(e); } try { key.channel().close(); } catch (Exception e) { logger.traceException(e); } } } catch (Exception e) { logger.traceException(e); } }
public boolean process(long timeout) throws IOException { while (timeout > 0) { long start = System.nanoTime(); int numKeys = selector.select(timeout); long end = System.nanoTime(); if (selector.keys().isEmpty()) return false; if (numKeys > 0) { dispatchMessages(); break; } timeout -= TimeUnit.NANOSECONDS.toMillis(end - start); if (timeout <= 0) { break; } } return true; }
/** * Check session timeout or idle * * @return */ private final long checkSessionTimeout() { long nextTimeout = 0; if (configuration.getCheckSessionTimeoutInterval() > 0) { gate.lock(); try { if (selectTries * 1000 >= configuration.getCheckSessionTimeoutInterval()) { nextTimeout = configuration.getCheckSessionTimeoutInterval(); for (SelectionKey key : selector.keys()) { if (key.attachment() != null) { long n = checkExpiredIdle(key, getSessionFromAttchment(key)); nextTimeout = n < nextTimeout ? n : nextTimeout; } } selectTries = 0; } } finally { gate.unlock(); } } return nextTimeout; }
protected void timeout(int keyCount, boolean hasEvents) { long now = System.currentTimeMillis(); // This method is called on every loop of the Poller. Don't process // timeouts on every loop of the Poller since that would create too // much load and timeouts can afford to wait a few seconds. // However, do process timeouts if any of the following are true: // - the selector simply timed out (suggests there isn't much load) // - the nextExpiration time has passed // - the server socket is being closed if (nextExpiration > 0 && (keyCount > 0 || hasEvents) && (now < nextExpiration) && !close) { return; } // timeout int keycount = 0; try { for (SelectionKey key : selector.keys()) { keycount++; try { NioSocketWrapper ka = (NioSocketWrapper) key.attachment(); if (ka == null) { cancelledKey(key); // we don't support any keys without attachments } else if (close) { key.interestOps(0); ka.interestOps(0); // avoid duplicate stop calls processKey(key, ka); } else if ((ka.interestOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ || (ka.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) { boolean isTimedOut = false; // Check for read timeout if ((ka.interestOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) { long delta = now - ka.getLastRead(); long timeout = ka.getReadTimeout(); isTimedOut = timeout > 0 && delta > timeout; } // Check for write timeout if (!isTimedOut && (ka.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) { long delta = now - ka.getLastWrite(); long timeout = ka.getWriteTimeout(); isTimedOut = timeout > 0 && delta > timeout; } if (isTimedOut) { key.interestOps(0); ka.interestOps(0); // avoid duplicate timeout calls ka.setError(new SocketTimeoutException()); if (!processSocket(ka, SocketEvent.ERROR, true)) { cancelledKey(key); } } } } catch (CancelledKeyException ckx) { cancelledKey(key); } } // for } catch (ConcurrentModificationException cme) { // See https://bz.apache.org/bugzilla/show_bug.cgi?id=57943 log.warn(sm.getString("endpoint.nio.timeoutCme"), cme); } long prevExp = nextExpiration; // for logging purposes only nextExpiration = System.currentTimeMillis() + socketProperties.getTimeoutInterval(); if (log.isTraceEnabled()) { log.trace( "timeout completed: keys processed=" + keycount + "; now=" + now + "; nextExpiration=" + prevExp + "; keyCount=" + keyCount + "; hasEvents=" + hasEvents + "; eval=" + ((now < prevExp) && (keyCount > 0 || hasEvents) && (!close))); } }
@SelectorThreadOnly private void abortAll(Throwable e) { Set<NioTransport> pairs = new HashSet<NioTransport>(); for (SelectionKey k : selector.keys()) pairs.add((NioTransport) k.attachment()); for (NioTransport p : pairs) p.abort(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"); } }
@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. } } } }
static void releaseTemporarySelector(Selector sel) throws IOException { // Selector should be empty sel.selectNow(); // Flush cancelled keys assert sel.keys().isEmpty() : "Temporary selector not empty"; localSelectorWrapper.set(null); }
@Override public void run() { LOG.debug(getName() + ": starting"); SERVER.set(Server.this); long lastPurgeTime = 0; // last check for old calls. while (running) { try { waitPending(); // If a channel is being registered, wait. writeSelector.select(PURGE_INTERVAL); Iterator<SelectionKey> iter = writeSelector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); iter.remove(); try { if (key.isValid() && key.isWritable()) { doAsyncWrite(key); } } catch (IOException e) { LOG.info(getName() + ": doAsyncWrite threw exception " + e); } } long now = System.currentTimeMillis(); if (now < lastPurgeTime + PURGE_INTERVAL) { continue; } lastPurgeTime = now; // // If there were some calls that have not been sent out for a // long time, discard them. // ArrayList<Call> calls; // get the list of channels from list of keys. synchronized (writeSelector.keys()) { calls = new ArrayList<Call>(writeSelector.keys().size()); iter = writeSelector.keys().iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); Call call = (Call) key.attachment(); if (call != null && key.channel() == call.connection.channel) { calls.add(call); } } } for (Call call : calls) { try { doPurge(call, now); } catch (IOException e) { LOG.warn("Error in purging old calls " + e); } } } catch (OutOfMemoryError e) { // // we can run out of memory if we have too many threads // log the event and sleep for a minute and give // some thread(s) a chance to finish // LOG.warn("Out of Memory in server select", e); try { Thread.sleep(60000); } catch (Exception ie) { } } catch (Exception e) { LOG.warn("Exception in Responder " + e.toString()); } } LOG.debug("Stopping " + this.getName()); this.shutDown = true; }
public void run() { while (!stopping) { // Execute any due timers. long timeout = execute_timers(); if (retired) { for (SelectableChannel ch : fd_table.keySet()) { SelectionKey key = ch.keyFor(selector); if (key == null) { try { key = ch.register(selector, pollset.get(ch)); key.attach(fd_table.get(ch)); } catch (ClosedChannelException e) { continue; } } else if (key.isValid()) { key.attach(fd_table.get(ch)); key.interestOps(pollset.get(ch)); } } for (SelectionKey key : selector.keys()) { if (!fd_table.containsKey(key.channel())) { // removed key.cancel(); } } retired = false; } // Wait for events. int rc; try { rc = selector.select(timeout); } catch (IOException e) { throw new ZError.IOException(e); } if (rc == 0) continue; Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = it.next(); IPollEvents evt = (IPollEvents) key.attachment(); it.remove(); try { if (key.isWritable()) { evt.out_event(); } if (key.isReadable()) { evt.in_event(); } else if (key.isAcceptable()) { evt.accept_event(); } else if (key.isConnectable()) { evt.connect_event(); } } catch (CancelledKeyException e) { // channel might have been closed } } } stopped = true; }
@Override 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 { SelectorUtil.select(selector); // '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(); processEventQueue(); 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) { 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 that lead to // excessive CPU consumption. try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore. } } } }
/** Shuntdown this instance by closing its Selector and associated channels. */ public void shutdown() { // If shutdown was called for this SelectorHandler if (isShutDown.getAndSet(true)) return; stateHolder.setState(State.STOPPED); if (selector != null) { try { boolean isContinue = true; while (isContinue) { try { for (SelectionKey selectionKey : selector.keys()) { selectionKeyHandler.close(selectionKey); } isContinue = false; } catch (ConcurrentModificationException e) { // ignore } } } catch (ClosedSelectorException e) { // If Selector is already closed - OK } } try { if (serverSocket != null) { serverSocket.close(); serverSocket = null; } } catch (Throwable ex) { Controller.logger().log(Level.SEVERE, "serverSocket.close", ex); } try { if (serverSocketChannel != null) { serverSocketChannel.close(); serverSocketChannel = null; } } catch (Throwable ex) { Controller.logger().log(Level.SEVERE, "serverSocketChannel.close", ex); } try { if (selector != null) selector.close(); } catch (Throwable ex) { Controller.logger().log(Level.SEVERE, "selector.close", ex); } if (asyncQueueReader != null) { asyncQueueReader.close(); asyncQueueReader = null; } if (asyncQueueWriter != null) { asyncQueueWriter.close(); asyncQueueWriter = null; } selectorHandlerTasks.clear(); attributes = null; }
/** * Look jvm bug * * @param before * @param selected * @param wait * @return * @throws IOException */ private boolean lookJVMBug(long before, int selected, long wait) throws IOException { boolean seeing = false; long now = System.currentTimeMillis(); if (JVMBUG_THRESHHOLD > 0 && selected == 0 && wait > JVMBUG_THRESHHOLD && now - before < wait / 4 && !wakenUp.get() /* waken up */ && !Thread.currentThread().isInterrupted() /* Interrupted */) { jvmBug.incrementAndGet(); if (jvmBug.get() >= JVMBUG_THRESHHOLD2) { gate.lock(); try { lastJVMBug = now; log.warn( "JVM bug occured at " + new Date(lastJVMBug) + ",http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933,reactIndex=" + reactorIndex); if (jvmBug1) { log.debug("seeing JVM BUG(s) - recreating selector,reactIndex=" + reactorIndex); } else { jvmBug1 = true; log.info("seeing JVM BUG(s) - recreating selector,reactIndex=" + reactorIndex); } seeing = true; final Selector new_selector = SystemUtils.openSelector(); for (SelectionKey k : selector.keys()) { if (!k.isValid() || k.interestOps() == 0) { continue; } final SelectableChannel channel = k.channel(); final Object attachment = k.attachment(); channel.register(new_selector, k.interestOps(), attachment); } selector.close(); selector = new_selector; } finally { gate.unlock(); } jvmBug.set(0); } else if (jvmBug.get() == JVMBUG_THRESHHOLD || jvmBug.get() == JVMBUG_THRESHHOLD1) { if (jvmBug0) { log.debug("seeing JVM BUG(s) - cancelling interestOps==0,reactIndex=" + reactorIndex); } else { jvmBug0 = true; log.info("seeing JVM BUG(s) - cancelling interestOps==0,reactIndex=" + reactorIndex); } gate.lock(); seeing = true; try { for (SelectionKey k : selector.keys()) { if (k.isValid() && k.interestOps() == 0) { k.cancel(); } } } finally { gate.unlock(); } } } else { jvmBug.set(0); } return seeing; }