Let's say we have a Java NIO Selector
that selects with a timeout on multiple SocketChannels
for read operations:
Selector selector = Selector.open();
channel1.register(selector, SelectionKey.OP_READ);
channel2.register(selector, SelectionKey.OP_READ);
channel3.register(selector, SelectionKey.OP_READ);
channel4.register(selector, SelectionKey.OP_READ);
// ... maybe even more ...
while (true) {
if (selector.select(TIMEOUT) > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isValid() && key.isReadable())
read(key);
iterator.remove();
}
} else {
// All channels timed-out! Cancel and close them all
cancelAndCloseAll(selector.keys());
}
}
We need to cancel and close a channel if it's been idle for a specific time, and that's why we're using the selector.select(TIMEOUT)
method.
But this doesn't work if we have a few very active channels. Those active channels will never let the select
to timeout, while all the other channels might be idle ...
A naive solution for this is as follows (also mentioned here) :
Attach the last time of activity to each channel's
SelectionKey
using thekey.attach(object)
method. After each successful select, update the activity time of all ready keys. Then iterate over all the keys and find the ones that have been idle more than the specific threshold.
This could be really inefficient because the active channels will cause the select
to fire very frequently and everytime iterating all over the keys set.
So is there a better (more efficient) way of solving this problem ?