2016-03-08 17 views
5

Secondo Javadoc,Selector.select (timeout) restituisce 0 prima del timeout

Esso restituisce solo dopo aver selezionato almeno un canale, il metodo sveglia di questo selettore viene richiamato, il thread corrente viene interrotta, oppure la data il periodo di timeout scade, a seconda dell'evento che si verifica per primo.

ma occasionalmente restituisce senza di questi 4 casi:

  1. almeno un canale viene selezionato: restituisce metodo 0
  2. riattivazione viene richiamato: wakeup non è chiamato
  3. il thread corrente viene interrotto: Thread.interrupted() restituisce false
  4. .515.053.691,36321 milioni
  5. determinato periodo di timeout: non superata in base ai registri

AGGIORNAMENTO 2016-03-15

Nella mia fonte alla linea 392 e la linea 402 ho aggiunto alcuni registri: 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) { 
     ... 

Ecco il 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 
... 

Questo è molto strano: nessun tasto selezionato, nessuna interruzione, nessun timeout e nessuna sveglia, ma è tornato.

C'è un errore in Java? La mia versione Java è 1.8.0_51-b16 (VM server a 64 bit) ed è eseguita su un linodo CentOS 6.5 x64.

+0

Bella domanda ben scritta +1 –

+0

Avvia processi esterni da questo processo? –

+0

@RoeeShenberg n. solo un singolo processo java e non chiamare altri processi. – auntyellow

risposta

1

Questo potrebbe essere un bug in JDK. Sembra che anche Netty e Mina incontrino un tale problema e ricostruiscono il selettore come soluzione alternativa.

Vedere ultimo codice Netty https://github.com/netty/netty/blob/4.1/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java L641-681: codice

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

Pertanto, la registrazione di un nuovo selettore può essere la migliore pratica, se selezionare () restituisce un imprevisto zero.

2

Il Javadoc è abbastanza chiaro.

Durante ciascuna operazione di selezione, è possibile aggiungere e rimuovere chiavi dal set di tasti selezionato di un selettore .... La selezione avviene select(), selezionare (lunghezza), e selectNow() metodi, e prevede tre passaggi:

  1. ...

  2. Il sistema operativo sottostante viene interrogato per un aggiornamento come alla prontezza di ciascun canale rimanente per eseguire una qualsiasi delle operazioni identificate dall'interesse della sua chiave al momento in cui è iniziata l'operazione di selezione. Per un canale che è pronto per almeno una tale operazione, una delle seguenti due azioni viene eseguita:

    1. Se la chiave del canale non è già nel tasto selezionato impostato, allora viene aggiunto a tale insieme e il suo set di pronto intervento viene modificato per identificare esattamente quelle operazioni per le quali il canale è ora segnalato essere pronto. Tutte le informazioni sulla prontezza precedentemente registrate nel set pronto vengono scartate.

    2. In caso contrario, la chiave del canale è già presente nel set di tasti selezionato, pertanto il set di operazioni preliminari viene modificato per identificare eventuali nuove operazioni per le quali il canale è segnalato come pronto. Tutte le informazioni sulla prontezza precedentemente registrate nel set pronto vengono mantenute; in altre parole, il set pronto restituito dal sistema sottostante è bitwise-disgiunto nel set corrente corrente della chiave.

Quello che sta accadendo è che sulla selezione che restituisce zero, la WASS tasto di selezione già nel set-tasto selezionato, quindi nessun cambiamento nel numero di chiavi pronti si è verificato.

Nota anche nella sezione per il select(int timeout) metodo (il corsivo è mio):

Returns:

  • Il numero dei tasti, possibilmente pari a zero, i cui set di pronto-operazione erano aggiornamento
+0

Nel mio programma, una volta selezionato, tutti i tasti selezionati vengono gestiti e cancellati. Quindi penso che la prossima selezione NON debba tornare a zero a meno del timeout. – auntyellow

+0

Il modo per controllare questo sarebbe, quando si ottiene un ritorno a zero imprevisto, per scorrere tutte le chiavi pronte ed esaminare le operazioni pronte per vedere cosa c'è. Come sei sicuro di aver gestito tutte le operazioni pronte? –

+0

Grazie. Avrò un tentativo ... – auntyellow