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 56 static long millisElapsedSince(long startTime) { 57 return (System.nanoTime() - startTime) / (1000L * 1000L); 58 } 59 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 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 81 static void awaitHasWaiters(ReentrantLock lock, Condition condition, 82 long timeoutMillis) { 83 spinWaitUntil(() -> hasWaiters(lock, condition), timeoutMillis); 84 } 85 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 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 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; 128 void pass() {passed++;} 129 void fail() {failed++; Thread.dumpStack();} 130 void fail(String msg) {System.err.println(msg); fail();} 131 void unexpected(Throwable t) {failed++; t.printStackTrace();} 132 void check(boolean cond) {if (cond) pass(); else fail();} 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);} 136 public static void main(String[] args) throws Throwable { 137 new ZeroCoreThreads().instanceMain(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 }