6824477: (se) Selector.select fails with IOException: "Invalid argument" if maximum file descriptors is low
authoralanb
Thu, 02 Apr 2009 19:47:24 +0100
changeset 2444 282b8abf92eb
parent 2443 4bcc75ed04c0
child 2445 a1fa6863fc50
6824477: (se) Selector.select fails with IOException: "Invalid argument" if maximum file descriptors is low Reviewed-by: sherman
jdk/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java
jdk/test/java/nio/channels/Selector/LotsOfUpdates.java
jdk/test/java/nio/channels/Selector/lots_of_updates.sh
--- a/jdk/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java	Thu Apr 02 16:31:44 2009 +0100
+++ b/jdk/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java	Thu Apr 02 19:47:24 2009 +0100
@@ -76,20 +76,19 @@
     // Base address of the native pollArray
     private long pollArrayAddress;
 
+    // Array of pollfd structs used for driver updates
+    private AllocatedNativeObject updatePollArray;
+
     // Maximum number of POLL_FD structs to update at once
-    private int MAX_UPDATE_SIZE = 10000;
+    private int MAX_UPDATE_SIZE = Math.min(OPEN_MAX, 10000);
 
     DevPollArrayWrapper() {
         int allocationSize = NUM_POLLFDS * SIZE_POLLFD;
         pollArray = new AllocatedNativeObject(allocationSize, true);
         pollArrayAddress = pollArray.address();
+        allocationSize = MAX_UPDATE_SIZE * SIZE_POLLFD;
+        updatePollArray = new AllocatedNativeObject(allocationSize, true);
         wfd = init();
-
-        for (int i=0; i<NUM_POLLFDS; i++) {
-            putDescriptor(i, 0);
-            putEventOps(i, 0);
-            putReventOps(i, 0);
-        }
     }
 
     // Machinery for remembering fd registration changes
@@ -129,21 +128,11 @@
         register(wfd, fd0, POLLIN);
     }
 
-    void putEventOps(int i, int event) {
-        int offset = SIZE_POLLFD * i + EVENT_OFFSET;
-        pollArray.putShort(offset, (short)event);
-    }
-
     void putReventOps(int i, int revent) {
         int offset = SIZE_POLLFD * i + REVENT_OFFSET;
         pollArray.putShort(offset, (short)revent);
     }
 
-    void putDescriptor(int i, int fd) {
-        int offset = SIZE_POLLFD * i + FD_OFFSET;
-        pollArray.putInt(offset, fd);
-    }
-
     int getEventOps(int i) {
         int offset = SIZE_POLLFD * i + EVENT_OFFSET;
         return pollArray.getShort(offset);
@@ -174,9 +163,10 @@
     void closeDevPollFD() throws IOException {
         FileDispatcherImpl.closeIntFD(wfd);
         pollArray.free();
+        updatePollArray.free();
     }
 
-    int poll(long timeout) {
+    int poll(long timeout) throws IOException {
         updateRegistrations();
         updated = poll0(pollArrayAddress, NUM_POLLFDS, timeout, wfd);
         for (int i=0; i<updated; i++) {
@@ -189,60 +179,34 @@
         return updated;
     }
 
-    void updateRegistrations() {
-        // take snapshot of the updateList size to see if there are
-        // any registrations to update
-        int updateSize;
+    void updateRegistrations() throws IOException {
+        // Populate pollfd array with updated masks
         synchronized (updateList) {
-            updateSize = updateList.size();
-        }
-        if (updateSize > 0) {
-            // Construct a pollfd array with updated masks; we may overallocate
-            // by some amount because if the events are already POLLREMOVE
-            // then the second pollfd of that pair will not be needed. The
-            // number of entries is limited to a reasonable number to avoid
-            // allocating a lot of memory.
-            int maxUpdates = Math.min(updateSize * 2, MAX_UPDATE_SIZE);
-            int allocationSize =  maxUpdates * SIZE_POLLFD;
-            AllocatedNativeObject updatePollArray =
-                new AllocatedNativeObject(allocationSize, true);
+            while (updateList.size() > 0) {
+                // We have to insert a dummy node in between each
+                // real update to use POLLREMOVE on the fd first because
+                // otherwise the changes are simply OR'd together
+                int index = 0;
+                Updator u = null;
+                while ((u = updateList.poll()) != null) {
+                    // First add pollfd struct to clear out this fd
+                    putPollFD(updatePollArray, index, u.fd, POLLREMOVE);
+                    index++;
+                    // Now add pollfd to update this fd, if necessary
+                    if (u.mask != POLLREMOVE) {
+                        putPollFD(updatePollArray, index, u.fd, (short)u.mask);
+                        index++;
+                    }
 
-            try {
-                synchronized (updateList) {
-                    while (updateList.size() > 0) {
-                        // We have to insert a dummy node in between each
-                        // real update to use POLLREMOVE on the fd first because
-                        // otherwise the changes are simply OR'd together
-                        int index = 0;
-                        Updator u = null;
-                        while ((u = updateList.poll()) != null) {
-                            // First add pollfd struct to clear out this fd
-                            putPollFD(updatePollArray, index, u.fd, POLLREMOVE);
-                            index++;
-                            // Now add pollfd to update this fd, if necessary
-                            if (u.mask != POLLREMOVE) {
-                                putPollFD(updatePollArray, index, u.fd,
-                                          (short)u.mask);
-                                index++;
-                            }
-
-                            // Check against the max allocation size; these are
-                            // all we will process. Valid index ranges from 0 to
-                            // (maxUpdates - 1) and we can use up to 2 per loop
-                            if (index > maxUpdates - 2)
-                                break;
-                        }
-                        // Register the changes with /dev/poll
-                        registerMultiple(wfd, updatePollArray.address(), index);
-                     }
+                    // Check against the max update size; these are
+                    // all we will process. Valid index ranges from 0 to
+                    // (MAX_UPDATE_SIZE - 1) and we can use up to 2 per loop
+                    if (index >  MAX_UPDATE_SIZE - 2)
+                        break;
                 }
-            } finally {
-                // Free the native array
-                updatePollArray.free();
-                // BUG: If an exception was thrown then the selector now believes
-                // that the last set of changes was updated but it probably
-                // was not. This should not be a likely occurrence.
-            }
+                // Register the changes with /dev/poll
+                registerMultiple(wfd, updatePollArray.address(), index);
+             }
         }
     }
 
@@ -275,7 +239,8 @@
 
     private native int init();
     private native void register(int wfd, int fd, int mask);
-    private native void registerMultiple(int wfd, long address, int len);
+    private native void registerMultiple(int wfd, long address, int len)
+        throws IOException;
     private native int poll0(long pollAddress, int numfds, long timeout,
                              int wfd);
     private static native void interrupt(int fd);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/channels/Selector/LotsOfUpdates.java	Thu Apr 02 19:47:24 2009 +0100
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.nio.channels.*;
+import java.io.IOException;
+
+public class LotsOfUpdates {
+    public static void main(String[] args) throws IOException {
+        Selector sel = Selector.open();
+        SocketChannel sc = SocketChannel.open();
+        sc.configureBlocking(false);
+        SelectionKey key = sc.register(sel, 0);
+        for (int i=0; i<50000; i++) {
+            key.interestOps(0);
+        }
+        sel.selectNow();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/channels/Selector/lots_of_updates.sh	Thu Apr 02 19:47:24 2009 +0100
@@ -0,0 +1,49 @@
+#
+# Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# 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.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+# @test
+# @bug 6824477
+# @summary Selector.select can fail with IOException "Invalid argument" on
+#     Solaris if maximum number of file descriptors is less than 10000
+# @build LotsOfUpdates
+# @run shell lots_of_updates.sh
+
+OS=`uname -s`
+case "$OS" in
+    Windows_* )
+        echo "ulimit not on Windows"
+        exit 0
+        ;;
+    * )
+        CLASSPATH=${TESTCLASSES}:${TESTSRC}
+        ;;
+esac
+export CLASSPATH
+
+# hard limit needs to be less than 10000 for this bug
+NOFILES=`ulimit -n -H`
+if [ "$NOFILES" = "unlimited" ] || [ $NOFILES -ge 10000 ]; then
+    ulimit -n 2048
+fi
+
+${TESTJAVA}/bin/java LotsOfUpdates