6823609: (se) Selector.select hangs on Windows under load
authoralanb
Fri, 03 Apr 2009 22:10:36 +0100
changeset 2445 a1fa6863fc50
parent 2444 282b8abf92eb
child 2446 07047237e4d4
6823609: (se) Selector.select hangs on Windows under load Reviewed-by: sherman
jdk/src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java
jdk/test/java/nio/channels/Selector/HelperSlowToDie.java
--- a/jdk/src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java	Thu Apr 02 19:47:24 2009 +0100
+++ b/jdk/src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java	Fri Apr 03 22:10:36 2009 +0100
@@ -34,7 +34,6 @@
 import java.nio.channels.ClosedSelectorException;
 import java.nio.channels.Pipe;
 import java.nio.channels.SelectableChannel;
-import java.nio.channels.SelectionKey;
 import java.io.IOException;
 import java.util.List;
 import java.util.ArrayList;
@@ -72,7 +71,7 @@
     private int threadsCount = 0;
 
     // A list of helper threads for select.
-    private final List<Thread> threads = new ArrayList<Thread>();
+    private final List<SelectThread> threads = new ArrayList<SelectThread>();
 
     //Pipe used as a wakeup object.
     private final Pipe wakeupPipe;
@@ -201,7 +200,7 @@
                         Thread.currentThread().interrupt();
                     }
                 }
-                if (thread.index >= threads.size()) { // redundant thread
+                if (thread.isZombie()) { // redundant thread
                     return true; // will cause run() to exit.
                 } else {
                     thread.lastRun = runsCounter; // update lastRun
@@ -388,9 +387,10 @@
 
     // Represents a helper thread used for select.
     private final class SelectThread extends Thread {
-        private int index; // index of this thread
-        SubSelector subSelector;
+        private final int index; // index of this thread
+        final SubSelector subSelector;
         private long lastRun = 0; // last run number
+        private volatile boolean zombie;
         // Creates a new thread
         private SelectThread(int i) {
             this.index = i;
@@ -398,6 +398,12 @@
             //make sure we wait for next round of poll
             this.lastRun = startLock.runsCounter;
         }
+        void makeZombie() {
+            zombie = true;
+        }
+        boolean isZombie() {
+            return zombie;
+        }
         public void run() {
             while (true) { // poll loop
                 // wait for the start of poll. If this thread has become
@@ -432,7 +438,7 @@
         } else if (threadsCount < threads.size()) {
             // Some threads become redundant. Remove them from the threads List.
             for (int i = threads.size() - 1 ; i >= threadsCount; i--)
-                threads.remove(i);
+                threads.remove(i).makeZombie();
         }
     }
 
@@ -468,10 +474,9 @@
         updateCount++;
         int numKeysUpdated = 0;
         numKeysUpdated += subSelector.processSelectedKeys(updateCount);
-        Iterator it = threads.iterator();
-        while (it.hasNext())
-            numKeysUpdated += ((SelectThread)it.next()).subSelector.
-                                             processSelectedKeys(updateCount);
+        for (SelectThread t: threads) {
+            numKeysUpdated += t.subSelector.processSelectedKeys(updateCount);
+        }
         return numKeysUpdated;
     }
 
@@ -495,13 +500,13 @@
                     }
                     pollWrapper.free();
                     pollWrapper = null;
-                     selectedKeys = null;
-                     channelArray = null;
-                     threads.clear();
-                     // Call startThreads. All remaining helper threads now exit,
-                     // since threads.size() = 0;
-                     startLock.startThreads();
-                 }
+                    selectedKeys = null;
+                    channelArray = null;
+                    // Make all remaining helper threads exit
+                    for (SelectThread t: threads)
+                         t.makeZombie();
+                    startLock.startThreads();
+                }
             }
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/channels/Selector/HelperSlowToDie.java	Fri Apr 03 22:10:36 2009 +0100
@@ -0,0 +1,75 @@
+/*
+ * 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 6823609
+ * @summary Selector.select can hangs on Windows for cases where a helper thread
+ *   becomes redudant but a new helper is immediately needed.
+ */
+
+import java.nio.channels.*;
+import java.io.IOException;
+
+public class HelperSlowToDie {
+    private static final int CHANNELS_PER_THREAD = 1023;
+    private static volatile boolean done;
+
+    public static void main(String[] args) throws IOException {
+        Selector sel = Selector.open();
+
+        // register channels
+        SocketChannel[] channels = new SocketChannel[CHANNELS_PER_THREAD];
+        for (int i=0; i<CHANNELS_PER_THREAD; i++) {
+            SocketChannel sc = SocketChannel.open();
+            sc.configureBlocking(false);
+            sc.register(sel, SelectionKey.OP_CONNECT);
+            channels[i] = sc;
+        }
+        sel.selectNow();
+
+        // Start threads to swamp all cores but one. This improves the chances
+        // of duplicating the bug.
+        Runnable busy = new Runnable() {
+            public void run() {
+                while (!done) ;  // no nothing
+            }
+        };
+        int ncores = Runtime.getRuntime().availableProcessors();
+        for (int i=0; i<ncores-1; i++)
+            new Thread(busy).start();
+
+        // Loop changing the number of channels from 1023 to 1024 and back.
+        for (int i=0; i<1000; i++) {
+            SocketChannel sc = SocketChannel.open();
+            sc.configureBlocking(false);
+            sc.register(sel, SelectionKey.OP_CONNECT);
+            sel.selectNow();   // cause helper to spin up
+            sc.close();
+            sel.selectNow();  // cause helper to retire
+        }
+
+        // terminate busy threads
+        done = true;
+    }
+}