7143744: (se) Stabilize KQueue SelectorProvider and make default on MacOSX
authoralanb
Mon, 16 Apr 2012 20:12:20 +0100
changeset 12438 c1183d4a80d4
parent 12437 9fb493dfdfa7
child 12439 07ab7fcb2f08
child 12538 211d6e82fe51
7143744: (se) Stabilize KQueue SelectorProvider and make default on MacOSX Reviewed-by: michaelm, chegar
jdk/src/macosx/classes/sun/nio/ch/DefaultSelectorProvider.java
jdk/src/macosx/classes/sun/nio/ch/KQueueArrayWrapper.java
jdk/src/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java
jdk/src/macosx/classes/sun/nio/ch/KQueueSelectorProvider.java
jdk/src/macosx/native/sun/nio/ch/KQueueArrayWrapper.c
jdk/test/java/nio/channels/Selector/OpRead.java
jdk/test/sun/nio/ch/SelProvider.java
--- a/jdk/src/macosx/classes/sun/nio/ch/DefaultSelectorProvider.java	Mon Apr 16 11:12:10 2012 +0800
+++ b/jdk/src/macosx/classes/sun/nio/ch/DefaultSelectorProvider.java	Mon Apr 16 20:12:20 2012 +0100
@@ -4,7 +4,9 @@
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
  *
  * This code is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -40,7 +42,7 @@
      * Returns the default SelectorProvider.
      */
     public static SelectorProvider create() {
-            return new sun.nio.ch.PollSelectorProvider();
+        return new sun.nio.ch.KQueueSelectorProvider();
     }
 
 }
--- a/jdk/src/macosx/classes/sun/nio/ch/KQueueArrayWrapper.java	Mon Apr 16 11:12:10 2012 +0800
+++ b/jdk/src/macosx/classes/sun/nio/ch/KQueueArrayWrapper.java	Mon Apr 16 20:12:20 2012 +0100
@@ -4,7 +4,9 @@
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
  *
  * This code is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -64,8 +66,8 @@
     static short FD_OFFSET;
     static short FILTER_OFFSET;
 
-    // kevent array size (just under 1K bytes)
-    static final int NUM_KEVENTS = 50;
+    // kevent array size
+    static final int NUM_KEVENTS = 128;
 
     // Are we in a 64-bit VM?
     static boolean is64bit = false;
--- a/jdk/src/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java	Mon Apr 16 11:12:10 2012 +0800
+++ b/jdk/src/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java	Mon Apr 16 20:12:20 2012 +0100
@@ -4,7 +4,9 @@
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
  *
  * This code is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -49,8 +51,8 @@
     // Count of registered descriptors (including interrupt)
     private int totalChannels;
 
-    // Map from file descriptors to selection keys
-    private HashMap<Integer,SelectionKeyImpl> fdToKey;
+    // Map from a file descriptor to an entry containing the selection key
+    private HashMap<Integer,MapEntry> fdMap;
 
     // True if this Selector has been closed
     private boolean closed = false;
@@ -59,6 +61,20 @@
     private Object interruptLock = new Object();
     private boolean interruptTriggered = false;
 
+    // used by updateSelectedKeys to handle cases where the same file
+    // descriptor is polled by more than one filter
+    private long updateCount;
+
+    // Used to map file descriptors to a selection key and "update count"
+    // (see updateSelectedKeys for usage).
+    private static class MapEntry {
+        SelectionKeyImpl ski;
+        long updateCount;
+        MapEntry(SelectionKeyImpl ski) {
+            this.ski = ski;
+        }
+    }
+
     /**
      * Package private constructor called by factory method in
      * the abstract superclass Selector.
@@ -70,7 +86,7 @@
         fd1 = (int)fds;
         kqueueWrapper = new KQueueArrayWrapper();
         kqueueWrapper.initInterrupt(fd0, fd1);
-        fdToKey = new HashMap<>();
+        fdMap = new HashMap<>();
         totalChannels = 1;
     }
 
@@ -82,8 +98,6 @@
         if (closed)
             throw new ClosedSelectorException();
         processDeregisterQueue();
-        if (timeout == 0  &&  totalChannels == 1)
-            return 0;
         try {
             begin();
             entries = kqueueWrapper.poll(timeout);
@@ -94,10 +108,9 @@
         return updateSelectedKeys(entries);
     }
 
-
     /**
-     * Update the keys whose fd's have been selected by the devpoll
-     * driver. Add the ready keys to the ready queue.
+     * Update the keys whose fd's have been selected by kqueue.
+     * Add the ready keys to the selected key set.
      * If the interrupt fd has been selected, drain it and clear the interrupt.
      */
     private int updateSelectedKeys(int entries)
@@ -106,24 +119,42 @@
         int numKeysUpdated = 0;
         boolean interrupted = false;
 
+        // A file descriptor may be registered with kqueue with more than one
+        // filter and so there may be more than one event for a fd. The update
+        // count in the MapEntry tracks when the fd was last updated and this
+        // ensures that the ready ops are updated rather than replaced by a
+        // second or subsequent event.
+        updateCount++;
+
         for (int i = 0; i < entries; i++) {
             int nextFD = kqueueWrapper.getDescriptor(i);
             if (nextFD == fd0) {
                 interrupted = true;
             } else {
-                SelectionKeyImpl ski = fdToKey.get(new Integer(nextFD));
-                // ski is null in the case of an interrupt
-                if (ski != null) {
+                MapEntry me = fdMap.get(Integer.valueOf(nextFD));
+
+                // entry is null in the case of an interrupt
+                if (me != null) {
                     int rOps = kqueueWrapper.getReventOps(i);
+                    SelectionKeyImpl ski = me.ski;
                     if (selectedKeys.contains(ski)) {
-                        if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
-                            numKeysUpdated++;
+                        // first time this file descriptor has been encountered on this
+                        // update?
+                        if (me.updateCount != updateCount) {
+                            if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
+                                numKeysUpdated++;
+                                me.updateCount = updateCount;
+                            }
+                        } else {
+                            // ready ops have already been set on this update
+                            ski.channel.translateAndUpdateReadyOps(rOps, ski);
                         }
                     } else {
                         ski.channel.translateAndSetReadyOps(rOps, ski);
-                        if ((ski.readyOps() & ski.interestOps()) != 0) {
+                        if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
                             selectedKeys.add(ski);
                             numKeysUpdated++;
+                            me.updateCount = updateCount;
                         }
                     }
                 }
@@ -137,7 +168,6 @@
                 interruptTriggered = false;
             }
         }
-
         return numKeysUpdated;
     }
 
@@ -145,6 +175,12 @@
     protected void implClose() throws IOException {
         if (!closed) {
             closed = true;
+
+            // prevent further wakeup
+            synchronized (interruptLock) {
+                interruptTriggered = true;
+            }
+
             FileDispatcherImpl.closeIntFD(fd0);
             FileDispatcherImpl.closeIntFD(fd1);
             if (kqueueWrapper != null) {
@@ -172,8 +208,10 @@
 
 
     protected void implRegister(SelectionKeyImpl ski) {
+        if (closed)
+            throw new ClosedSelectorException();
         int fd = IOUtil.fdVal(ski.channel.getFD());
-        fdToKey.put(new Integer(fd), ski);
+        fdMap.put(Integer.valueOf(fd), new MapEntry(ski));
         totalChannels++;
         keys.add(ski);
     }
@@ -181,7 +219,7 @@
 
     protected void implDereg(SelectionKeyImpl ski) throws IOException {
         int fd = ski.channel.getFDVal();
-        fdToKey.remove(new Integer(fd));
+        fdMap.remove(Integer.valueOf(fd));
         kqueueWrapper.release(fd);
         totalChannels--;
         keys.remove(ski);
@@ -194,6 +232,8 @@
 
 
     public void putEventOps(SelectionKeyImpl ski, int ops) {
+        if (closed)
+            throw new ClosedSelectorException();
         int fd = IOUtil.fdVal(ski.channel.getFD());
         kqueueWrapper.setInterest(fd, ops);
     }
--- a/jdk/src/macosx/classes/sun/nio/ch/KQueueSelectorProvider.java	Mon Apr 16 11:12:10 2012 +0800
+++ b/jdk/src/macosx/classes/sun/nio/ch/KQueueSelectorProvider.java	Mon Apr 16 20:12:20 2012 +0100
@@ -4,7 +4,9 @@
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
  *
  * This code is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
--- a/jdk/src/macosx/native/sun/nio/ch/KQueueArrayWrapper.c	Mon Apr 16 11:12:10 2012 +0800
+++ b/jdk/src/macosx/native/sun/nio/ch/KQueueArrayWrapper.c	Mon Apr 16 20:12:20 2012 +0100
@@ -4,7 +4,9 @@
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
  *
  * This code is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
--- a/jdk/test/java/nio/channels/Selector/OpRead.java	Mon Apr 16 11:12:10 2012 +0800
+++ b/jdk/test/java/nio/channels/Selector/OpRead.java	Mon Apr 16 20:12:20 2012 +0100
@@ -58,7 +58,10 @@
             boolean done = false;
             int failCount = 0;
             while (!done) {
-                if (selector.select() > 0) {
+                int nSelected = selector.select();
+                if (nSelected > 0) {
+                    if (nSelected > 1)
+                        throw new RuntimeException("More than one channel selected");
                     Set<SelectionKey> keys = selector.selectedKeys();
                     Iterator<SelectionKey> iterator = keys.iterator();
                     while (iterator.hasNext()) {
--- a/jdk/test/sun/nio/ch/SelProvider.java	Mon Apr 16 11:12:10 2012 +0800
+++ b/jdk/test/sun/nio/ch/SelProvider.java	Mon Apr 16 20:12:20 2012 +0100
@@ -38,20 +38,9 @@
         if ("SunOS".equals(osname)) {
             expected = "sun.nio.ch.DevPollSelectorProvider";
         } else if ("Linux".equals(osname)) {
-            String[] vers = osver.split("\\.", 0);
-            if (vers.length >= 2) {
-                int major = Integer.parseInt(vers[0]);
-                int minor = Integer.parseInt(vers[1]);
-                if (major > 2 || (major == 2 && minor >= 6)) {
-                    expected = "sun.nio.ch.EPollSelectorProvider";
-                } else {
-                    expected = "sun.nio.ch.PollSelectorProvider";
-                }
-            } else {
-                throw new RuntimeException("Test does not recognize this operating system");
-            }
+            expected = "sun.nio.ch.EPollSelectorProvider";
         } else if (osname.startsWith("Mac OS")) {
-            expected = "sun.nio.ch.PollSelectorProvider";
+            expected = "sun.nio.ch.KQueueSelectorProvider";
         } else
             return;
         if (!spName.equals(expected))