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 }