package bt.net;

import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.SelectorProvider;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Selector decorator with some convenient extensions, like {@link #wakeupAndRegister(SelectableChannel, int, Object)}.
 *
 * @since 1.5
 */
public class SharedSelector extends Selector {

    private final Selector delegate;
    private final ReentrantLock registrationLock;

    public SharedSelector(Selector delegate) {
        this.delegate = delegate;
        this.registrationLock = new ReentrantLock();
    }

    @Override
    public boolean isOpen() {
        return delegate.isOpen();
    }

    @Override
    public SelectorProvider provider() {
        return delegate.provider();
    }

    @Override
    public Set<SelectionKey> keys() {
        return delegate.keys();
    }

    @Override
    public Set<SelectionKey> selectedKeys() {
        return delegate.selectedKeys();
    }

    @Override
    public int selectNow() throws IOException {
        registrationLock.lock();
        try {
            return delegate.selectNow();
        } finally {
            registrationLock.unlock();
        }
    }

    @Override
    public int select(long timeout) throws IOException {
        registrationLock.lock();
        try {
            return delegate.select(timeout);
        } finally {
            registrationLock.unlock();
        }
    }

    @Override
    public int select() throws IOException {
        registrationLock.lock();
        try {
            return delegate.select();
        } finally {
            registrationLock.unlock();
        }
    }

    @Override
    public Selector wakeup() {
        return delegate.wakeup();
    }

    /**
     * Atomically wakeup and register the provided channel.
     *
     * @since 1.5
     */
    public void wakeupAndRegister(SelectableChannel channel, int ops, Object attachment) {
        while (!registrationLock.tryLock()) {
            delegate.wakeup();
        }
        try {
            channel.register(delegate, ops, attachment);
        } catch (ClosedChannelException e) {
            throw new RuntimeException("Failed to register channel", e);
        } finally {
            registrationLock.unlock();
        }
    }

    @Override
    public void close() throws IOException {
        delegate.close();
    }
}

@@ -313,24 +313,33 @@
 ks.Reentrant
+ReadWrite
 Lock;%0A%0A/**%0A 
@@ -586,24 +586,33 @@
 al Reentrant
+ReadWrite
 Lock registr
@@ -621,16 +621,63 @@
 ionLock;
+%0A    private volatile boolean selectInProgress;
 %0A%0A    pu
@@ -796,16 +796,25 @@
 eentrant
+ReadWrite
 Lock();%0A
@@ -1280,32 +1280,44 @@
 egistrationLock.
+writeLock().
 lock();%0A        
@@ -1314,32 +1314,69 @@
 ;%0A        try %7B%0A
+            selectInProgress = true;%0A
             retu
@@ -1412,32 +1412,70 @@
     %7D finally %7B%0A
+            selectInProgress = false;%0A
             regi
@@ -1479,32 +1479,44 @@
 egistrationLock.
+writeLock().
 unlock();%0A      
@@ -1614,32 +1614,44 @@
 egistrationLock.
+writeLock().
 lock();%0A        
@@ -1648,32 +1648,69 @@
 ;%0A        try %7B%0A
+            selectInProgress = true;%0A
             retu
@@ -1750,32 +1750,70 @@
     %7D finally %7B%0A
+            selectInProgress = false;%0A
             regi
@@ -1817,32 +1817,44 @@
 egistrationLock.
+writeLock().
 unlock();%0A      
@@ -1948,16 +1948,28 @@
 ionLock.
+writeLock().
 lock();%0A
@@ -1974,32 +1974,69 @@
 ;%0A        try %7B%0A
+            selectInProgress = true;%0A
             retu
@@ -2069,32 +2069,70 @@
     %7D finally %7B%0A
+            selectInProgress = false;%0A
             regi
@@ -2136,32 +2136,44 @@
 egistrationLock.
+writeLock().
 unlock();%0A      
@@ -2494,16 +2494,27 @@
 ionLock.
+readLock().
 tryLock(
@@ -2514,24 +2514,227 @@
 ryLock()) %7B%0A
+            // try to prevent lots of wakeup calls,%0A            // when multiple channels are being registered simultaneously;%0A            // no guarantees though%0A            if (selectInProgress) %7B%0A    
             
@@ -2748,24 +2748,38 @@
 e.wakeup();%0A
+            %7D%0A
         %7D%0A  
@@ -3014,16 +3014,27 @@
 ionLock.
+readLock().
 unlock()

Use I/O selector for receiving incoming messages