This fixes some issues with the epoll selector, related to handling
canceled keys properly, and handling keys registered to closed channels.

I'm also taking the approach where the events byte buffer -- a direct
byte buffer whose pointer we pass directly to epoll_wait -- is allocated
once when the selector is created, and is reallocated as needed when new
keys are registered. We keep doubling the capacity as needed, until the
buffer grows to a certain size, then we start incrementally increasing
the capacity; if the capacity is more than twice what is needed, the
buffer is reallocated to half its current size. This should allocating
(potentially large) buffers on each call to select(), and shouldn't
waste too much memory.

2006-09-29  Casey Marshall  <[EMAIL PROTECTED]>

        PR 29190
        * gnu/java/nio/EpollSelectionKeyImpl.java: extend
        `AbstractSelectionKey.'
        (cancel, isValid): removed.
        * gnu/java/nio/EpollSelectorImpl.java (cancelledKeys): removed.
        (events): new field.
        (INITIAL_CAPACITY, MAX_DOUBLING_CAPACITY, CAPACITY_INCREMENT): new
        fields.
        (<clinit>): initialize those constants.
        (<init>): don't initialize `cancelledKeys;' initialize `events.'
        (doSelect): deregister cancelled keys; remove keys attached to
        closed channels; wrap `epoll_wait' in `begin' and `end' calls; use
        `events' buffer; reallocate `events' buffer if needed.
        (register): reallocate `events' buffer if needed.
        (reallocateBuffer): new method.
        (cancel): removed.
Index: gnu/java/nio/EpollSelectionKeyImpl.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/nio/EpollSelectionKeyImpl.java,v
retrieving revision 1.1
diff -u -r1.1 EpollSelectionKeyImpl.java
--- gnu/java/nio/EpollSelectionKeyImpl.java     20 Sep 2006 21:39:41 -0000      
1.1
+++ gnu/java/nio/EpollSelectionKeyImpl.java     30 Sep 2006 05:13:49 -0000
@@ -43,11 +43,12 @@
 import java.nio.channels.SelectableChannel;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.Selector;
+import java.nio.channels.spi.AbstractSelectionKey;
 
 /**
  * @author Casey Marshall ([EMAIL PROTECTED])
  */
-public class EpollSelectionKeyImpl extends SelectionKey
+public class EpollSelectionKeyImpl extends AbstractSelectionKey
 {
   final int fd;
   private final EpollSelectorImpl selector;
@@ -67,15 +68,6 @@
   }
 
   /* (non-Javadoc)
-   * @see java.nio.channels.SelectionKey#cancel()
-   */
-  public void cancel()
-  {
-    cancelled = true;
-    selector.cancel(this);
-  }
-
-  /* (non-Javadoc)
    * @see java.nio.channels.SelectionKey#channel()
    */
   public SelectableChannel channel()
@@ -113,14 +105,6 @@
   }
 
   /* (non-Javadoc)
-   * @see java.nio.channels.SelectionKey#isValid()
-   */
-  public boolean isValid()
-  {
-    return valid;
-  }
-
-  /* (non-Javadoc)
    * @see java.nio.channels.SelectionKey#readyOps()
    */
   public int readyOps()
Index: gnu/java/nio/EpollSelectorImpl.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/nio/EpollSelectorImpl.java,v
retrieving revision 1.4
diff -u -r1.4 EpollSelectorImpl.java
--- gnu/java/nio/EpollSelectorImpl.java 27 Sep 2006 21:30:44 -0000      1.4
+++ gnu/java/nio/EpollSelectorImpl.java 30 Sep 2006 05:13:49 -0000
@@ -42,6 +42,7 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.channels.SelectableChannel;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.Selector;
 import java.nio.channels.spi.AbstractSelectableChannel;
@@ -75,8 +76,12 @@
   
   private final HashMap keys;
   private Set selectedKeys;
-  private final Set cancelledKeys;
   private Thread waitingThread;
+  private ByteBuffer events;
+  
+  private static final int INITIAL_CAPACITY;
+  private static final int MAX_DOUBLING_CAPACITY;
+  private static final int CAPACITY_INCREMENT;
 
   static
   {
@@ -87,6 +92,10 @@
       sizeof_struct_epoll_event = sizeof_struct();
     else
       sizeof_struct_epoll_event = -1;
+    
+    INITIAL_CAPACITY = 64 * sizeof_struct_epoll_event;
+    MAX_DOUBLING_CAPACITY = 1024 * sizeof_struct_epoll_event;
+    CAPACITY_INCREMENT = 128 * sizeof_struct_epoll_event;
   }
   
   public EpollSelectorImpl(SelectorProvider provider)
@@ -96,7 +105,7 @@
     epoll_fd = epoll_create(DEFAULT_EPOLL_SIZE);
     keys = new HashMap();
     selectedKeys = null;
-    cancelledKeys = new HashSet();
+    events = ByteBuffer.allocateDirect(INITIAL_CAPACITY);
   }
 
   /* (non-Javadoc)
@@ -131,6 +140,7 @@
   {
     synchronized (keys)
     {
+      Set cancelledKeys = cancelledKeys();
       synchronized (cancelledKeys)
       {
         for (Iterator it = cancelledKeys.iterator(); it.hasNext(); )
@@ -140,25 +150,45 @@
             key.valid = false;
             keys.remove(Integer.valueOf(key.fd));
             it.remove();
+            deregister(key);
+          }
+        
+        // Clear out closed channels. The fds are removed from the epoll
+        // fd when closed, so there is no need to remove them manually.
+        for (Iterator it = keys.values().iterator(); it.hasNext(); )
+          {
+            EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next();
+            SelectableChannel ch = key.channel();
+            if (ch instanceof VMChannelOwner)
+              {
+                if (!((VMChannelOwner) ch).getVMChannel().getState().isValid())
+                  it.remove();
+              }
           }
         
         // Don't bother if we have nothing to select.
         if (keys.isEmpty())
           return 0;
 
-        ByteBuffer selected =
-          ByteBuffer.allocateDirect(keys.size() * sizeof_struct_epoll_event);
-      
-        waitingThread = Thread.currentThread();
-        int ret = epoll_wait(epoll_fd, selected, keys.size(), timeout);
-        Thread.interrupted();
-        waitingThread = null;
+        int ret;
+        try
+          {
+            begin();
+            waitingThread = Thread.currentThread();
+            ret = epoll_wait(epoll_fd, events, keys.size(), timeout);
+          }
+        finally
+          {
+            Thread.interrupted();
+            waitingThread = null;
+            end();
+          }
       
         HashSet s = new HashSet(ret);
         for (int i = 0; i < ret; i++)
           {
-            selected.position(i * sizeof_struct_epoll_event);
-            ByteBuffer b = selected.slice();
+            events.position(i * sizeof_struct_epoll_event);
+            ByteBuffer b = events.slice();
             int fd = selected_fd(b);
             EpollSelectionKeyImpl key
               = (EpollSelectionKeyImpl) keys.get(Integer.valueOf(fd));
@@ -167,6 +197,9 @@
             key.selectedOps = selected_ops(b) & key.interestOps;
             s.add(key);
           }
+        
+        reallocateBuffer();
+        
         selectedKeys = s;
         return ret;
       }
@@ -202,6 +235,7 @@
       }
     catch (NullPointerException npe)
       {
+        // Ignored, thrown if we are not in a blocking op.
       }
     return this;
   }
@@ -241,6 +275,7 @@
           result.key = System.identityHashCode(result);
           epoll_add(epoll_fd, result.fd, ops);
           keys.put(Integer.valueOf(native_fd), result);
+         reallocateBuffer();
           return result;
         }
       }
@@ -250,17 +285,32 @@
       }
   }
   
-  void epoll_modify(EpollSelectionKeyImpl key, int ops) throws IOException
+  private void reallocateBuffer()
   {
-    epoll_modify(epoll_fd, key.fd, ops);
+    // Ensure we have enough space for all potential events that may be
+    // returned.
+    if (events.capacity() < keys.size() * sizeof_struct_epoll_event)
+      {
+        int cap = events.capacity();
+        if (cap < MAX_DOUBLING_CAPACITY)
+          cap <<= 1;
+        else
+          cap += CAPACITY_INCREMENT;
+        events = ByteBuffer.allocateDirect(cap);
+      }
+    // Ensure that the events buffer is not too large, given the number of
+    // events registered.
+    else if (events.capacity() > keys.size() * sizeof_struct_epoll_event * 2 + 
1
+            && events.capacity() > INITIAL_CAPACITY)
+      {
+        int cap = events.capacity() >>> 1;
+        events = ByteBuffer.allocateDirect(cap);
+      }
   }
   
-  void cancel(EpollSelectionKeyImpl key)
+  void epoll_modify(EpollSelectionKeyImpl key, int ops) throws IOException
   {
-    synchronized (cancelledKeys)
-    {
-      cancelledKeys.add(key);
-    }
+    epoll_modify(epoll_fd, key.fd, ops);
   }
   
   /**

Reply via email to