Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selector.select do not block as expected

In my current project I notice that select() do not block as expected. It do not block at all and return always, even when no IO was present. So I got a busy cpu.

The registration will always invoked by another thread, so I need the lock and the wakeup.

The doc says for selectNow():

Invoking this method clears the effect of any previous invocations of the wakeup method.

So I invoke the method at the end of each iteration. no succsess. I found no example or explanation how to use selectNow for my purpose.

What is wrong with the code?


Here is my example code, so you can test this.

BTW: Another stackoverflow question was the rolemodel of my code. EDIT: Example fixed! It works now.

import java.io.IOException;
import java.net.*;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.locks.ReentrantLock;

public class Test implements Runnable {
    ReentrantLock selectorLock = new ReentrantLock();
    Selector selector;
    boolean alive;

    @Override
    public void run() {
        SelectionKey key;
        Iterator<SelectionKey> keys;

        alive = true;
        try {
            while (alive) {
                selectorLock.lock();
                selectorLock.unlock();

                selector.select();
                System.out.println("select() returned");

                keys = selector.selectedKeys().iterator();
                // handle each "event"
                while (keys.hasNext()) {
                    key = keys.next();
                    // mark as handled
                    keys.remove();
                    // handle
                    handleKey(key);
                }
                //selector.selectNow(); // don't fix this
            }
        } catch ( IOException e ) {
            e.printStackTrace();
        }
    }

    private void handleKey(SelectionKey key)
        throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        if (key.isConnectable()) {
            System.out.println("connecting");
            if ( channel.finishConnect() ) {
                key.interestOps(SelectionKey.OP_READ);
            } else {
                key.cancel();
            }
        } else if (key.isReadable()) {
            System.out.println("reading");
            // read and detect remote close
            channel.read(ByteBuffer.allocate(64));
        }
    }

    public void register(SelectableChannel channel, int ops, Object attachment)
        throws ClosedChannelException {
        selectorLock.lock();
        try {
            System.out.println("wakeup");
            selector.wakeup();
            channel.register(selector, ops, attachment);
        } finally {
            selectorLock.unlock();
        }
    }

    public Test()
        throws IOException {
        selector = Selector.open();
    }

    public static void main(String[] args)
        throws IOException {
        Test t = new Test();
        new Thread(t).start();

        SocketAddress address = new InetSocketAddress("localhost", 8080);
        SocketChannel channel = SocketChannel.open();
        channel.configureBlocking(false);
        channel.connect(address);

        t.register(channel, SelectionKey.OP_CONNECT, "test channel attachment");
    }
}
like image 470
Marcel Jaeschke Avatar asked Aug 30 '11 20:08

Marcel Jaeschke


1 Answers

Don't register OP_READ until OP_CONNECT has fired and finishConnect() has returned 'true'. At that point you must deregister OP_CONNECT.

Similarly don't register channels for OP_WRITE until you have something to write. OP_WRITE is always ready except when the socket send buffer is full, so it should only be registered after you have detected that condition (write() returns zero), and you should de-register it immediately it fires (unless the condition happens again).

And finally OP_CONNECT and OP_WRITE are the same thing under the hood, which given what I've just said about OP_WRITE explains your selector spins.

like image 180
user207421 Avatar answered Sep 21 '22 04:09

user207421