1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 only, as
6  * published by the Free Software Foundation.
7  *
8  * This code is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * version 2 for more details (a copy is included in the LICENSE file that
12  * accompanied this code).
13  *
14  * You should have received a copy of the GNU General Public License version
15  * 2 along with this work; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19  * or visit www.oracle.com if you need additional information or have any
20  * questions.
21  */
22 
23 /*
24  * This file is available under and governed by the GNU General Public
25  * License version 2 only, as published by the Free Software Foundation.
26  * However, the following notice accompanied the original version of this
27  * file:
28  *
29  * Written by Martin Buchholz with assistance from members of JCP
30  * JSR-166 Expert Group and released to the public domain, as
31  * explained at http://creativecommons.org/publicdomain/zero/1.0/
32  */
33 
34 /*
35  * @test
36  * @bug 8022642 8065320 8129861
37  * @summary Ensure relative sanity when zero core threads
38  * @library /test/lib
39  * @modules java.base/java.util.concurrent:open
40  */
41 
42 import static java.util.concurrent.TimeUnit.HOURS;
43 import static java.util.concurrent.TimeUnit.MILLISECONDS;
44 
45 import java.lang.reflect.Field;
46 import java.util.concurrent.BlockingQueue;
47 import java.util.concurrent.ScheduledThreadPoolExecutor;
48 import java.util.concurrent.locks.Condition;
49 import java.util.concurrent.locks.ReentrantLock;
50 import java.util.function.BooleanSupplier;
51 import jdk.test.lib.Utils;
52 
53 public class ZeroCoreThreads {
54     static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
55 
millisElapsedSince(long startTime)56     static long millisElapsedSince(long startTime) {
57         return (System.nanoTime() - startTime) / (1000L * 1000L);
58     }
59 
spinWaitUntil(BooleanSupplier predicate, long timeoutMillis)60     static void spinWaitUntil(BooleanSupplier predicate, long timeoutMillis) {
61         long startTime = -1L;
62         while (!predicate.getAsBoolean()) {
63             if (startTime == -1L)
64                 startTime = System.nanoTime();
65             else if (millisElapsedSince(startTime) > timeoutMillis)
66                 throw new AssertionError(
67                     String.format("timed out after %s ms", timeoutMillis));
68             Thread.yield();
69         }
70     }
71 
hasWaiters(ReentrantLock lock, Condition condition)72     static boolean hasWaiters(ReentrantLock lock, Condition condition) {
73         lock.lock();
74         try {
75             return lock.hasWaiters(condition);
76         } finally {
77             lock.unlock();
78         }
79     }
80 
awaitHasWaiters(ReentrantLock lock, Condition condition, long timeoutMillis)81     static void awaitHasWaiters(ReentrantLock lock, Condition condition,
82                                 long timeoutMillis) {
83         spinWaitUntil(() -> hasWaiters(lock, condition), timeoutMillis);
84     }
85 
getField(Object x, String fieldName)86     static <T> T getField(Object x, String fieldName) {
87         try {
88             Field field = x.getClass().getDeclaredField(fieldName);
89             field.setAccessible(true);
90             return (T) field.get(x);
91         } catch (ReflectiveOperationException ex) {
92             throw new AssertionError(ex);
93         }
94     }
95 
test(String[] args)96     void test(String[] args) throws Throwable {
97         ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(0);
98         try {
99             test(p);
100         } finally {
101             p.shutdownNow();
102             check(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
103         }
104     }
105 
test(ScheduledThreadPoolExecutor p)106     void test(ScheduledThreadPoolExecutor p) throws Throwable {
107         Runnable dummy = new Runnable() { public void run() {
108             throw new AssertionError("shouldn't get here"); }};
109         BlockingQueue q = p.getQueue();
110         ReentrantLock lock = getField(q, "lock");
111         Condition available = getField(q, "available");
112 
113         equal(0, p.getPoolSize());
114         equal(0, p.getLargestPoolSize());
115         equal(0L, p.getTaskCount());
116         equal(0L, p.getCompletedTaskCount());
117         p.schedule(dummy, 1L, HOURS);
118         // Ensure one pool thread actually waits in timed queue poll
119         awaitHasWaiters(lock, available, LONG_DELAY_MS);
120         equal(1, p.getPoolSize());
121         equal(1, p.getLargestPoolSize());
122         equal(1L, p.getTaskCount());
123         equal(0L, p.getCompletedTaskCount());
124     }
125 
126     //--------------------- Infrastructure ---------------------------
127     volatile int passed = 0, failed = 0;
pass()128     void pass() {passed++;}
fail()129     void fail() {failed++; Thread.dumpStack();}
fail(String msg)130     void fail(String msg) {System.err.println(msg); fail();}
unexpected(Throwable t)131     void unexpected(Throwable t) {failed++; t.printStackTrace();}
check(boolean cond)132     void check(boolean cond) {if (cond) pass(); else fail();}
equal(Object x, Object y)133     void equal(Object x, Object y) {
134         if (x == null ? y == null : x.equals(y)) pass();
135         else fail(x + " not equal to " + y);}
main(String[] args)136     public static void main(String[] args) throws Throwable {
137         new ZeroCoreThreads().instanceMain(args);}
instanceMain(String[] args)138     void instanceMain(String[] args) throws Throwable {
139         try {test(args);} catch (Throwable t) {unexpected(t);}
140         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
141         if (failed > 0) throw new AssertionError("Some tests failed");}
142 }
143