This discussion is archived
9 Replies Latest reply: Jun 19, 2012 3:43 AM by gimbal2 RSS

Java NIO strange behaviour

648941 Newbie
Currently Being Moderated
Hi,

my server code follows the well known pattern as follows;
while (enabled) 
    try {
        int count = selector.select();

        Iterator<SelectionKey> it = selector.selectedKeys().iterator();
        while (it.hasNext())
        {
            SelectionKey key = (SelectionKey) it.next();                        

            it.remove();
                        
            if (key.isAcceptable())
            {
                ...                            
                final SelectionKey newKey = channel.register(
                             selector, SelectionKey.OP_READ, cd);
                ...
                continue;
            }

            if (key.isReadable()) {
                      
                key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
                            
                threadPool.execute((Runnable) key.attachment());
            }

            if (key.isWritable()) {                   
                ...
            }
        }                    
    }

} catch (Exception ex) {
    logger.error(ex, ex);
}
The reading from the channel is delegated to a thread worker provided by the thread pool.
It executes a code from a state object attached to the key. At the end of the read process I setup the READ interest back to true:
...
key.interestOps(key.interestOps() | SelectionKey.OP_READ);        

key.selector().wakeup();
...
The problem is, once I have the READ selector key interest disabled and then enabled the key get never again selected for readiness even there is data waiting on the socket.

I checked with tcpdump and can confirm that the data is arrived but the channel get never back selected by the selector.

Any ideas what could cause this behavior?
  • 1. Re: Java NIO strange behaviour
    sabre150 Expert
    Currently Being Moderated
    I have only been involved in NIO for a few weeks and initially had the same problem you had. EJP helped me and he is probably the best person to deal with this but I bet your problem comes from invoking this
    key.interestOps(key.interestOps() | SelectionKey.OP_READ); 
    outside of the 'select' thread since the interestOps(...) method blocks waiting for the lock that the 'select' thread holds (the read does no block, only the write). You can confirm this by using a debugger and putting a break point on the wakup() call; you will find it is not reached.

    I spent a lot of time researching this on the web but failed to find a satisfactory solution so came up with my own. My solution, most probably not standard, is to add the ability to inject functionality into the main select loop that runs before the rest of the loop. I embed the key.interestOps(key.interestOps() | SelectionKey.OP_READ); in the run() method of a Runnable and then add it to a list of Runnable to be executed (and then discarded) in the select loop. All I then have to do is to wakeup() the loop.

    Obviously the functionality in the Runnable.run() method should do as little as is necessary.

    I have managed to get rid of all threads associated with the select loop except where there is a need to interact with IO that cannot be run using NIO. In my case the primary one is interacting with Process objects created using ProcessBuilder.

    Edited by: sabre150 on Jun 19, 2012 9:24 AM
  • 2. Re: Java NIO strange behaviour
    EJP Guru
    Currently Being Moderated
    my server code follows the well known pattern as follows;
    There are aspects of this that are not 'well-known' to me.
    threadPool.execute((Runnable) key.attachment());
    Why? You already know there is data, you can read it without blocking: why start another thread to do what you can do perfectly well right here?
    The reading from the channel is delegated to a thread worker provided by the thread pool.
    Why?
    It executes a code from a state object attached to the key. At the end of the read process I setup the READ interest back to true:
    Which will block due to the synchronization specified in the Javadoc for interestOps() and select(). Which is why you don't want to do this in a separate thread. NIO is all about a single thread. Do it that way. If you have zilliions of connections, or dozens of CPUs, run several selector threads and have them all handle their own connections. Don't even think about delegating I/O to other threads though, it is infinitely more trouble than it is worth.
  • 3. Re: Java NIO strange behaviour
    648941 Newbie
    Currently Being Moderated
    if I put a debug statement right after the .select() call:
    int count = selector.select();
                        
    logger.debug(String.format("%d keys selected", count));
    I see it each time the thread worker completes :
    02:02:33,328 DEBUG Server:312 - 0 keys selected
    It seems .weakup() properly interrupts the main thread. Even if I try to make a timeout on select just to ensure that the selector will make another cycle and will read the actual selectorKey flags it does not select the channel which is quite strange:
    int count = selector.select(1000);
    result:
    16:41:11,636  INFO Server:310 - selected 0
    16:41:12,637  INFO Server:310 - selected 0
    16:41:13,638  INFO Server:310 - selected 0
    16:41:14,639  INFO Server:310 - selected 0
    16:41:15,640  INFO Server:310 - selected 0
    16:41:16,641  INFO Server:310 - selected 0
    16:41:17,642  INFO Server:310 - selected 0
    16:41:18,643  INFO Server:310 - selected 0
    16:41:19,644  INFO Server:310 - selected 0
    16:41:20,645  INFO Server:310 - selected 0
    16:41:21,646  INFO Server:310 - selected 0
    16:41:22,647  INFO Server:310 - selected 0
    16:41:23,648  INFO Server:310 - selected 0
  • 4. Re: Java NIO strange behaviour
    648941 Newbie
    Currently Being Moderated
    I can read in main thread, it is not a problem. After that I can delegate the data to a worker thread for handling.

    Reading in the main thread will be a bottle neck, right? All incoming to the server messages will get in one after another. Is this correct?
  • 5. Re: Java NIO strange behaviour
    EJP Guru
    Currently Being Moderated
    Yes it's correct but it would happen that way anyway. You are stuck with the order of the selected-key set returned by the Selector, and you are processing it sequentially. Nothing can change that. In my experience you are better off fiddling with the interestOps as little as possible.
  • 6. Re: Java NIO strange behaviour
    648941 Newbie
    Currently Being Moderated
    Thank you EJP. I will move the reading to the main thread. This will definitely solve the problem. Honestly said if I comment the line :
    key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
    all is working well and the client and the server exchange messages asynchronously. What makes me curious is that this code was working almost an year :) It seems switching to Java 7 has changes something. The pattern I mentioned is taken from the O'REALLY book Java NIO. Never-mind, thank you very much for answering my question.
  • 7. Re: Java NIO strange behaviour
    gimbal2 Guru
    Currently Being Moderated
    coderman wrote:
    It seems switching to Java 7 has changes something.
    It is always a possibility that there is some sort of regression because in Java7 the NIO API was revamped. But plenty of times when people post about "It was working in JavaX but it is not working in JavaY", they were actually doing it wrong all along and the real issue was that the JVM went along with it anyway.
  • 8. Re: Java NIO strange behaviour
    648941 Newbie
    Currently Being Moderated
    I did move the reading to the main thread. Now all is working fine. I hope this would not cause much troubles when the server reach 100k connections and 5% of them are simultaneously active. LOL
  • 9. Re: Java NIO strange behaviour
    gimbal2 Guru
    Currently Being Moderated
    coderman wrote:
    I hope this would not cause much troubles when the server reach 100k connections
    If that happens please post back so we can all toast to your epic success, but until then I'll just say: it probably ain't gonna ever happen ;)

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points