7091003: ScheduledExecutorService never executes Runnable with corePoolSize of zero
authordl
Fri, 23 Sep 2011 14:24:04 +0100
changeset 10609 0caee7f44e7b
parent 10608 7cfca36fc79b
child 10610 13b9a7aee6f3
7091003: ScheduledExecutorService never executes Runnable with corePoolSize of zero Reviewed-by: dholmes, chegar
jdk/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java
jdk/src/share/classes/java/util/concurrent/ThreadPoolExecutor.java
jdk/test/java/util/concurrent/ScheduledThreadPoolExecutor/ZeroCorePoolSize.java
--- a/jdk/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java	Wed Sep 21 23:22:11 2011 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java	Fri Sep 23 14:24:04 2011 +0100
@@ -330,7 +330,7 @@
                 remove(task))
                 task.cancel(false);
             else
-                prestartCoreThread();
+                ensurePrestart();
         }
     }
 
@@ -346,7 +346,7 @@
             if (!canRunInCurrentRunState(true) && remove(task))
                 task.cancel(false);
             else
-                prestartCoreThread();
+                ensurePrestart();
         }
     }
 
--- a/jdk/src/share/classes/java/util/concurrent/ThreadPoolExecutor.java	Wed Sep 21 23:22:11 2011 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ThreadPoolExecutor.java	Fri Sep 23 14:24:04 2011 +0100
@@ -1546,6 +1546,18 @@
     }
 
     /**
+     * Same as prestartCoreThread except arranges that at least one
+     * thread is started even if corePoolSize is 0.
+     */
+    void ensurePrestart() {
+        int wc = workerCountOf(ctl.get());
+        if (wc < corePoolSize)
+            addWorker(null, true);
+        else if (wc == 0)
+            addWorker(null, false);
+    }
+
+    /**
      * Starts all core threads, causing them to idly wait for work. This
      * overrides the default policy of starting core threads only when
      * new tasks are executed.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/ScheduledThreadPoolExecutor/ZeroCorePoolSize.java	Fri Sep 23 14:24:04 2011 +0100
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7091003
+ * @summary ScheduledExecutorService never executes Runnable
+ *          with corePoolSize of zero
+ * @author Chris Hegarty
+ */
+
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Verify that tasks can be run even with a core pool size of 0.
+ */
+public class ZeroCorePoolSize {
+
+    volatile boolean taskRun;
+
+    void test(String[] args) throws Throwable {
+
+        ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(0);
+        Runnable task = new Runnable() {
+            public void run() {
+                taskRun = true;
+            }
+        };
+        check(pool.getCorePoolSize() == 0);
+
+        pool.schedule(task, 1, TimeUnit.SECONDS);
+
+        pool.shutdown();
+        check(pool.awaitTermination(20L, TimeUnit.SECONDS));
+        check(pool.getCorePoolSize() == 0);
+        check(taskRun);
+    }
+
+    //--------------------- Infrastructure ---------------------------
+    volatile int passed = 0, failed = 0;
+    void pass() {passed++;}
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+    void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    void check(boolean cond) {if (cond) pass(); else fail();}
+    void equal(Object x, Object y) {
+        if (x == null ? y == null : x.equals(y)) pass();
+        else fail(x + " not equal to " + y);}
+    public static void main(String[] args) throws Throwable {
+        new ZeroCorePoolSize().instanceMain(args);}
+    void instanceMain(String[] args) throws Throwable {
+        try {test(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+    abstract class F {abstract void f() throws Throwable;}
+    void THROWS(Class<? extends Throwable> k, F... fs) {
+        for (F f : fs)
+            try {f.f(); fail("Expected " + k.getName() + " not thrown");}
+            catch (Throwable t) {
+                if (k.isAssignableFrom(t.getClass())) pass();
+                else unexpected(t);}}
+}