tags:

views:

417

answers:

3

If I would write:

int selectedChannels = selector.select();
Set selectedKeys = selector.selectedKeys();
if ( selectedChannels != selectedKeys.size() ) {
    // Selector.select() returned because of a call to Selector.wakeup()
    // so do synchronization.
}
// Continue with handling selected channels.

would it correctly detect the wakeup-call?

Backgroundinformation:

I'm writing a server which most of the time just receives packets and stores them in a file. Very rarely the application has the need to send itself a special packet. For this it initiates a connection (from a different thread) to the server socket:

SocketChannel channel = SocketChannel.open();
channel.configureBlocking( false );
channel.connect( new InetSocketAddress( InetAddress.getLocalHost(), PORT ));
selector.wakeup();
SelectionKey key = channel.register( selector, SelectionKey.OP_CONNECT );

The problem is that SelectableChannel.register() might block if the main thread is already in Selector.select(). To prevent this from happening I'm calling Selector.wakeup() which let's the main thread return prematurely from select(). To make sure the other thread has the chance to complete the register-call, I would have to synchronize the main thread, but I would have to do it after every return from select(). If I could detect whether it returned from select() because of a wakeup() call, then I could optimize it for just this case.

So, in theory the top code snippet should work, but I was wondering whether it would only do so, because it relies on some unspecified behavior?

Thanks for any hints.

A: 

I don't understand why your code would work in general.

Why not just check a volatile after select?

Tom Hawtin - tackline
I'm looking for an 'implicit' solution, meaning I want to use the information which is available anyway without any 'explicit' markers like boolean variables.
BugSlayer
A: 

You can't really be sure that the only reason that the selector woke up was due to the wakeup call. You may also have socket activity.

So, you need to make the caller of wakeup also do something like setting a volatile boolean to indicate its desire for attention. The the selector loop can check the value of this boolean every time it wakes up.

Darron
+2  A: 

I would guess that the proposed snippet would not work at all in principle, per the contracts of Selector#select() and Selector#selectedKeys(). From Selector:

  • The selected-key set is the set of keys such that each key's channel was detected to be ready for at least one of the operations identified in the key's interest set during a prior selection operation. This set is returned by the selectedKeys method.
public abstract int select(long timeout)
                throws IOException
    Returns:
        The number of keys, possibly zero, whose ready-operation sets were
        updated

As I read that, the size of the selectedKeys set should always equal the number returned by select by definition. I have noticed - as you may have as well - that some implementations don't quite follow the documentation, and in fact selectedKeys returns all keys with updated ready-operation sets, even if they were not updated during a call to select. The only other indicator that the select woke up due to a call to wakeup might be that the number of keys is zero; however either method would be unreliable, at best.

The usual way to handle this is, as implied, through concurrency control. I wouldn't worry about execution time here; this is a classic example of premature optimization.

Unless you're really worried about single digit microsecond tolerances, you won't notice any slowdown - and if you are worried about that level of tolerance, a Selector isn't going to be reliable enough for you anyway.

Here's an example of the usual mechanism for this, using a ReentrantLock to accomplish the appropriate concurrency:

ReentrantLock selectorGuard;
Selector selector;

private void doSelect() {
    // Don't enter a select if another thread is in a critical block
    selectorGuard.lock();
    selectorGuard.unlock();

    selector.select();
    Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

    while(keyIter.hasNext()) {

        SelectionKey key = keyIter.next();
        keyIter.remove();

        // Process key
    }
}

private void addToSelector() {

    // Lock the selector guard to prevent another select until complete
    selectorGuard.lock();

    try {
        selector.wakeup();

        // Do logic that registers channel with selector appropriately

    } finally {
        selectorGuard.unlock();
    }
}
Greg Case