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");
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With