Selector.select (timeout) returns 0 before the timeout

According to Javadok,

It returns only after at least one channel is selected, this method of triggering the selector is called, the current thread is interrupted or the specified waiting period expires, whichever comes first.

But sometimes he returns without any of these four cases:

  • at least one channel is selected: it returns 0
  • Awakening method called: wakeup not called
  • the current thread is interrupted: Thread.interrupted () returns false
  • set timeout expires: not expired according to logs

UPDATED 2016-03-15

In my source on line 392 and line 402 I added some logs: https://github.com/xqbase/tuna/blob/debug/core/src/main/java/com/xqbase/tuna/ConnectorImpl.java

 public boolean doEvents(long timeout) { Log.v("Before Select: " + timeout); int keySize; try { keySize = timeout == 0 ? selector.selectNow() : timeout < 0 ? selector.select() : selector.select(timeout); } catch (IOException e) { throw new RuntimeException(e); } Set<SelectionKey> selectedKeys = selector.selectedKeys(); if (keySize == 0) { Log.v("After Select(0): selectedKeys=" + selectedKeys.size() + ", " + "interrupt=" + Thread.interrupted()); invokeQueue(); return false; } for (SelectionKey key : selectedKeys) { ... 

Here is the log:

 ... 2016-03-15 23:07:49.695 com.xqbase.tuna.ConnectorImpl doEvents FINE: Before Select: 8120 2016-03-15 23:07:49.696 com.xqbase.tuna.ConnectorImpl doEvents FINE: After Select(0): selectedKeys=0, interrupt=false 2016-03-15 23:07:49.696 com.xqbase.tuna.ConnectorImpl doEvents FINE: Before Select: 8119 2016-03-15 23:07:49.696 com.xqbase.tuna.ConnectorImpl doEvents FINE: After Select(0): selectedKeys=0, interrupt=false 2016-03-15 23:07:49.700 com.xqbase.tuna.ConnectorImpl doEvents FINE: Before Select: 8115 2016-03-15 23:07:49.701 com.xqbase.tuna.ConnectorImpl doEvents FINE: After Select(0): selectedKeys=0, interrupt=false 2016-03-15 23:07:49.701 com.xqbase.tuna.ConnectorImpl doEvents FINE: Before Select: 8114 2016-03-15 23:07:49.702 com.xqbase.tuna.ConnectorImpl doEvents FINE: After Select(0): selectedKeys=0, interrupt=false ... 

This is very strange: no keys selected, without interruption, without a timeout and without waking up, but he returned.

Is there a bug in Java? My Java version is 1.8.0_51-b16 (64-bit server VM) and runs on linux CentOS 6.5 x64.

+5
source share
2 answers

This may indeed be a bug in the JDK. It seems that Netty and Mina are also facing this problem, and they are rebuilding the selector as a workaround.

See the latest Netty code https://github.com/netty/netty/blob/4.1/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java L641-681:

  if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) { // - Selected something, // - waken up by user, or // - the task queue has a pending task. // - a scheduled task is ready for processing break; } ... } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) { // The selector returned prematurely many times in a row. // Rebuild the selector to work around the problem. logger.warn( "Selector.select() returned prematurely {} times in a row; rebuilding selector.", selectCnt); rebuildSelector(); selector = this.selector; // Select again to populate selectedKeys. selector.selectNow(); selectCnt = 1; break; } 

See the Mina 2.0 code https://github.com/apache/mina/blob/2.0/mina-core/src/main/java/org/apache/mina/core/polling/AbstractPollingIoProcessor.java L1070-1092:

  if (!wakeupCalled.getAndSet(false) && (selected == 0) && (delta < 100)) { // Last chance : the select() may have been // interrupted because we have had an closed channel. if (isBrokenConnection()) { LOG.warn("Broken connection"); } else { LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0)); // Ok, we are hit by the nasty epoll // spinning. // Basically, there is a race condition // which causes a closing file descriptor not to be // considered as available as a selected channel, // but // it stopped the select. The next time we will // call select(), it will exit immediately for the // same // reason, and do so forever, consuming 100% // CPU. // We have to destroy the selector, and // register all the socket on a new one. registerNewSelector(); } } 

Therefore, registering a new selector may be best practice if select () returns an unexpected zero.

+1
source

Javadoc is pretty clear.

During each selection operation, keys can be added and removed from the set of selected selection keys .... Selection is performed using the select (), select (long) and selectNow () methods and includes three steps:

  • ...

  • The main operating system is requested to update regarding the readiness of each remaining channel to perform any of the operations identified by its key interest, starting from the moment the selection operation begins. For a channel that is ready for at least one such operation, one of the following two actions is performed:

    • If the channel key is not already set in the selected key, it is added to this set, and its set of ready-made operations is changed to accurately identify those operations for which it is now reported that the channel is ready. Any readiness information previously recorded in the finished set is discarded.

    • Otherwise, the channel key is already in the set of selected keys, so its set of ready-made operations is changed to identify any new operations for which it is reported that the channel is ready. Any readiness information previously recorded in the finished set is stored; in other words, the ready set returned by the base system is bit-sorted into the current current ready set of keys.

What happens is that when you select which returns zero, the select key was already installed in the selected key, so no changes in the number of ready keys occurred.

Note also the select(int timeout) section of the method (my emphasis):

Return:

  • The number of keys, possibly zero whose finished operations have been updated
+2
source

Source: https://habr.com/ru/post/1244627/


All Articles