JDK-6670302 : (se) NIO selector wakes up with 0 selected keys infinitely [lnx 2.4]
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: linux
  • CPU: x86
  • Submitted: 2008-03-03
  • Updated: 2013-08-20
  • Resolved: 2013-08-20
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_03"
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
Java HotSpot(TM) Server VM (build 1.6.0_03-b05, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
1. Linux hostname 2.4.21-27.ELsmp #1 SMP Wed Dec 1 21:59:02 EST 2004 i686 i686 i386 GNU/Linux

2. HP-UX hostname2 B.11.23 U ia64 2718609364 unlimited-user license


A DESCRIPTION OF THE PROBLEM :
The NIO selector wakes up infinitely in this situation..

0. server waits for connection
1. client connects and write message
2. server accepts and register OP_READ
3. server reads message and remove OP_READ from interest op set
4. client close the connection
5. server write message (without any reading.. surely OP_READ is not set)
6. server's select wakes up infinitely with return value 0


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
First run the TestServer.
 java -cp . TestServer 7777

In another console, run the TestClient
 java -cp . TestClient 192.168.1.34 7777



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I expected nothing will happen (the selector should not woke up).

ACTUAL -
In windows, it worked as I expected but, in Linux and HP/UX the selector goes into the infinite loop.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
TestServer.java

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
import java.util.logging.*;

public class TestServer {
    private static final long SLEEP_PERIOD = 5000L; // 5 seconds
    private static final int BUFFER_SIZE = 8192;
    private int port;

    public TestServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws Throwable {
	if (args.length < 1) {
	    System.err.println("Usage : java TestServer <port>");
	    System.exit(0);
	}

        new TestServer(Integer.parseInt(args[0])).start();
    }

    public void start() throws Throwable {
	ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);

        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);
        ServerSocket server = serverChannel.socket();
        server.bind(new InetSocketAddress(port));

        Selector selector = Selector.open();
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        SocketChannel clientChannel = null;

        System.out.println("0. SERVER STARTED TO LISTEN");
        boolean writeNow = false;

	while (true) {
	    try {
		// wait for selection
		int numKeys = selector.select();

                if (numKeys == 0) {
                    System.err.println("select wakes up with zero!!!");
                }

		Iterator it = selector.selectedKeys().iterator();
		while (it.hasNext()) {
		    SelectionKey selected = (SelectionKey) it.next();
		    int ops = selected.interestOps();

                    try {
                        // process new connection
                        if ((ops & SelectionKey.OP_ACCEPT) != 0) {
                            clientChannel = serverChannel.accept();
                            clientChannel.configureBlocking(false);

                            // register channel to selector
                            clientChannel.register(selector, SelectionKey.OP_READ, null);
                            System.out.println("2. SERVER ACCEPTED AND REGISTER READ OP : client - " + clientChannel.socket().getInetAddress());
                        }

                        if ((ops & SelectionKey.OP_READ) != 0) {
                            // read client message
                            System.out.println("3. SERVER READ DATA FROM client - " + clientChannel.socket().getInetAddress());
                            readClient((SocketChannel) selected.channel(), buffer);

                            // deregister OP_READ
                            System.out.println("PREV SET : " + selected.interestOps());
                            selected.interestOps(selected.interestOps() & ~SelectionKey.OP_READ);
                            System.out.println("NEW SET : " + selected.interestOps());

                            Thread.sleep(SLEEP_PERIOD * 2);
                            new WriterThread(clientChannel).start();
                        }

                    } finally {
                        // remove from selected key set
                        it.remove();
                    }
		}
	    } catch (IOException e) {
		System.err.println("IO Error : " + e.getMessage());
	    }
	}
    }


    public void readClient(SocketChannel channel, ByteBuffer buffer) throws IOException {
        try {
            buffer.clear();

            int nRead = channel.read(buffer);

            if (nRead < 0) {
                channel.close();
                return;
            }

            if (buffer.position() != 0) {
                int size = buffer.position();
                buffer.flip();
                byte[] bytes = new byte[size];
                buffer.get(bytes);
                System.out.println("RECVED : " + new String(bytes));
            }
        } catch (IOException e) {
            System.err.println("IO Error : " + e.getMessage());
            channel.close();
        }
    }

    static class WriterThread extends Thread {
        private SocketChannel clientChannel;
        public WriterThread(SocketChannel clientChannel) {
            this.clientChannel = clientChannel;
        }

        public void run() {
            try {
                writeClient(clientChannel);
                System.out.println("5. SERVER WRITE DATA TO client - " + clientChannel.socket().getInetAddress());
            } catch (IOException e) {
                System.err.println("5. SERVER WRITE DATA FAILED : " + e);
            }
        }

        public void writeClient(SocketChannel channel) throws IOException {
            try {
                ByteBuffer buffer = ByteBuffer.wrap("zwxydfdssdfsd".getBytes());
                int total = buffer.limit();

                int totalWrote = 0;
                int nWrote = 0;

                while ((nWrote = channel.write(buffer)) >= 0) {
                    totalWrote += nWrote;
                    if (totalWrote == total) {
                        break;
                    }
                }
            } catch (IOException e) {
                System.err.println("IO Error : " + e.getMessage());
                channel.close();
            }
        }


    }
}


TestClient.java

import java.util.logging.*;
import java.io.*;
import java.net.*;

public class TestClient {
    private static final long SLEEP_PERIOD = 5000L; // 5 seconds
    private String host;
    private int port;

    public TestClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public static void main(String[] args) throws Throwable {
	if (args.length < 2 || args[0].equals("127.0.0.1") || args[0].equals("localhost")) {
	    System.err.println("Usage : java TestClient <host name> <port> (host name should not be localhost)");
	    System.exit(0);
	}

        new TestClient(args[0], Integer.parseInt(args[1])).start();
    }

    public void start() throws Throwable {
        Socket socket = new Socket(host, port);

	BufferedReader in = new BufferedReader(
	    new InputStreamReader(socket.getInputStream()));
	PrintWriter out = new PrintWriter(
	    new OutputStreamWriter(socket.getOutputStream()),
	    true /* auto flush */);

        out.println("abcdef");

        System.out.println("1. CLIENT CONNECTED AND WROTE MESSAGE");

        Thread.sleep(SLEEP_PERIOD);

//         socket.shutdownOutput();
        socket.close();

        System.out.println("4. CLIENT SHUTDOWN OUTPUT");

        Thread.sleep(SLEEP_PERIOD * 3);
    }
}


---------- END SOURCE ----------

Comments
WORK AROUND Deploy on distribution that uses 2.6 kernel rather than 2.4 kernel.
03-03-2008

EVALUATION This appears to be a duplicate of 6481709 that has already been fixed for when running on the 2.6 kernel. The changes to fix the poll based Selector (the Selector that is used when running on the 2.4 kernel) were more extensive/risky and did not go into 6.0 update 1 with the changes to fix the epoll based Selector. Also since distributions have been shipping with the 2.6 kernel for almost 4 years it was assumed that the need for this fix would reduce significantly over time.
03-03-2008