JDK-4960791 : (se) Using OP_CONNECT with Selector.select causes selector to fire repeatedly
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux,windows_xp
  • CPU: x86
  • Submitted: 2003-11-27
  • Updated: 2006-12-11
  • Resolved: 2006-12-11
Related Reports
Duplicate :  
Description
Name: dk106046			Date: 11/27/2003

java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)



/* This testcase illustrates a problem with non-blocking connection to a server.
We start a simple server, listening on port 8765, which just accepts 
connections.  We then register a non-blocking SocketChannel (the client) with a
selector with interest in OP_CONNECT, so that we can use the selector to notify 
us when the channel is ready to finish connecting. We call client.connect and
then selector.select in a loop. The selector fires and with the client channel
in the selected-keys set and we call finishConnect() on the client's channel. 
Then the problem occurs: the selector repeatedly fires with no entries in its 
selected-keys set, whereas it should block in the next select operation until 
there is at least one key in the selected-keys set. */

import java.net.*;
import java.util.Iterator;
import java.nio.channels.*;

public class OpConnectTest {

    public static int PORT = 8765;

    public static void main( String[] args ) {
        new OpConnectTest() . runTest();
    }

    /* Start a server running in a separate thread. Wait for it to start then
    do a non-blocking connect to it. */
    public void runTest() {
        try{
            new Thread( new Runnable() {
                public void run() {
                    new AcceptServer() . startServer();
                }
            } ) . start();
            // Give the server enough time to start up.
            Thread.sleep( 200 );  
            connectNonBlockingClient();
            connectFinished = true;
        } catch ( Exception e ) {
            System.out.println( e );
        }
    }

    public static boolean connectFinished = false;

    /* Create a SocketChannel, register it with a Selector for OP_CONNECT, then
    do a non-blocking connect. */
    public void connectNonBlockingClient() throws Exception
    {
        SocketChannel channel = SocketChannel.open( );
        channel.configureBlocking( false );

        Selector selector = Selector.open();
        channel.register( selector, SelectionKey.OP_CONNECT );
        
        channel.connect( new InetSocketAddress( "localhost", PORT ) );

        for ( int j=1; j < 10; j++ ) {
            selector.select();
            System.out.println( "selector.SelectedKeys().size() = " 
                               + selector.selectedKeys().size() );
            
            Iterator i = selector.selectedKeys().iterator();
            while ( i.hasNext() ) {
                SelectionKey key = (SelectionKey)i.next();
                i.remove();
                try {
                    if ( ((SocketChannel)key.channel()).finishConnect() )
                        System.out.println( "connected" );
                    else
                        System.out.println( "not connected" );       
                } catch ( ConnectException e ) {
                    System.out.println( "encountered problem connecting" );
                    throw e;
                }
                System.out.println( "key.isValid() = " + key.isValid() 
                  + ", key.channel().isOpen() = " + key.channel().isOpen() );
            }
        }
    }
}

class AcceptServer {
    public void startServer() {
        try{
            ServerSocketChannel server = ServerSocketChannel.open();
            server.configureBlocking( false );
            System.out.println( "Starting AcceptServer listening on port " 
                               + OpConnectTest.PORT );
            server.socket().bind( new InetSocketAddress( OpConnectTest.PORT ) );
            while ( !OpConnectTest.connectFinished ) {
                server.accept();  // Accept connection, then do nothing with it.
            }
        } catch ( Exception e ) {
            System.out.println( e );
        }
    }
}


======================================================================

Comments
EVALUATION -- This is indeed a duplicate of 4919127. In the test case the Selector appears to be spinning because the SocketChannel is ready for writing once the connection is established. If the test is changed to invoke key.interestOps(0) after finishConnect returns true then the issue will go away.
11-12-2006

WORK AROUND Name: dk106046 Date: 11/27/2003 The key registered for OP_CONNECT must be cancelled when it is first returned by the selector to avoid this problem. Any program which fails to do this will suffer because the key is never returned in the selected-keys set again, and therefore is likely to be unobtainable (the selector will always fire immediately causing very bad performance). ======================================================================
25-09-2004

EVALUATION Possible duplicate of 4919127. -- ###@###.### 2003/12/10
12-11-0176