changeset 275:bec06db416c7

Generators/Runners: detect and use Thread.onSpinWait(), if available.
author shade
date Mon, 30 May 2016 18:30:11 +0300
parents c496ad24fb3b
children e0c28f4e127d
files jcstress-core/src/main/java/org/openjdk/jcstress/Options.java jcstress-core/src/main/java/org/openjdk/jcstress/infra/processors/JCStressTestProcessor.java jcstress-core/src/main/java/org/openjdk/jcstress/infra/runners/SpinLoopStyle.java jcstress-core/src/main/java/org/openjdk/jcstress/infra/runners/StateHolder.java jcstress-core/src/main/java/org/openjdk/jcstress/infra/runners/TestConfig.java jcstress-core/src/main/java/org/openjdk/jcstress/vm/ThreadSpinWaitTestMain.java jcstress-core/src/main/java/org/openjdk/jcstress/vm/VMSupport.java
diffstat 7 files changed, 160 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/Options.java	Mon May 30 15:25:00 2016 +0300
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/Options.java	Mon May 30 18:30:11 2016 +0300
@@ -28,6 +28,7 @@
 import joptsimple.OptionParser;
 import joptsimple.OptionSet;
 import joptsimple.OptionSpec;
+import org.openjdk.jcstress.infra.runners.SpinLoopStyle;
 import org.openjdk.jcstress.util.OptionFormatter;
 import org.openjdk.jcstress.util.Promise;
 import org.openjdk.jcstress.util.StringUtils;
@@ -234,12 +235,7 @@
     }
 
     public void printSettingsOn(PrintStream out) {
-        out.printf("  Hardware threads in use/available: %d/%d, ", getUserCPUs(), getSystemCPUs());
-        if (userYield) {
-            out.printf("user requested yielding in busy loops.\n");
-        } else {
-            out.printf("no yielding in use.\n");
-        }
+        out.printf("  Hardware threads in use/available: %d/%d, %s%n", getUserCPUs(), getSystemCPUs(), getSpinStyle());
         out.printf("  Test preset mode: \"%s\"\n", mode);
         out.printf("  Writing the test results to \"%s\"\n", resultFile);
         out.printf("  Parsing results to \"%s\"\n", resultDir);
@@ -269,8 +265,16 @@
         return time;
     }
 
-    public boolean shouldYield() {
-        return userYield;
+    public SpinLoopStyle getSpinStyle() {
+        if (VMSupport.spinWaitHintAvailable()) {
+            return SpinLoopStyle.THREAD_SPIN_WAIT;
+        } else {
+            if (userYield) {
+                return SpinLoopStyle.THREAD_YIELD;
+            } else {
+                return SpinLoopStyle.PLAIN;
+            }
+        }
     }
 
     public boolean shouldParse() {
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/infra/processors/JCStressTestProcessor.java	Mon May 30 15:25:00 2016 +0300
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/infra/processors/JCStressTestProcessor.java	Mon May 30 18:30:11 2016 +0300
@@ -368,7 +368,7 @@
         if (!isStateItself) {
             pw.println("        test = new " + t + "();");
         }
-        pw.println("        version = new StateHolder<>(false, new Pair[0], " + actorsCount + ");");
+        pw.println("        version = new StateHolder<>(false, new Pair[0], " + actorsCount + ", config.spinLoopStyle);");
         pw.println("        epoch = 0;");
 
         for (ExecutableElement a : info.getActors()) {
@@ -463,7 +463,8 @@
         pw.println("            }");
         pw.println("         }");
         pw.println();
-        pw.println("        version = new StateHolder<>(control.isStopped, newPairs, " + actorsCount + ");");
+        pw.println("        version = new StateHolder<>(control.isStopped, newPairs, " + actorsCount + ", config.spinLoopStyle);");
+        pw.println("        holder.notConsumed = false;");
         pw.println("   }");
 
         int n = 0;
@@ -475,8 +476,6 @@
             if (!isStateItself) {
                 pw.println("        " + t + " lt = test;");
             }
-            pw.println("        boolean yield = config.shouldYield;");
-            pw.println();
             pw.println("        while (true) {");
             pw.println("            StateHolder<Pair> holder = version;");
             pw.println("            if (holder.stopped) {");
@@ -485,7 +484,7 @@
             pw.println();
             pw.println("            Pair[] pairs = holder.pairs;");
             pw.println();
-            pw.println("            holder.preRun(yield);");
+            pw.println("            holder.preRun();");
             pw.println();
             pw.println("            for (Pair p : pairs) {");
 
@@ -512,7 +511,7 @@
 
             pw.println("            }");
             pw.println();
-            pw.println("            holder.postRun(yield);");
+            pw.println("            holder.postRun();");
             pw.println();
             pw.println("            jcstress_consume(holder, counter_" + a.getSimpleName() + ", " + n + ", " + actorsCount + ");");
             pw.println();
@@ -523,9 +522,8 @@
             pw.println("            }");
             pw.println();
             pw.println("            curEpoch += " + (actorsCount + 1) + ";");
-            pw.println("            while (curEpoch != EPOCH.get(this)) {");
-            pw.println("                if (yield) Thread.yield();");
-            pw.println("            }");
+            pw.println();
+            pw.println("            holder.postConsume();");
             pw.println("        }");
             pw.println("    }");
             n++;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/infra/runners/SpinLoopStyle.java	Mon May 30 18:30:11 2016 +0300
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014, 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package org.openjdk.jcstress.infra.runners;
+
+public enum SpinLoopStyle {
+    PLAIN("no yielding."),
+    THREAD_YIELD("using Thread.yield()."),
+    THREAD_SPIN_WAIT("using Thread.onSpinWait()"),
+    ;
+
+    private String desc;
+
+    SpinLoopStyle(String desc) {
+        this.desc = desc;
+    }
+
+    @Override
+    public String toString() {
+        return desc;
+    }
+}
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/infra/runners/StateHolder.java	Mon May 30 15:25:00 2016 +0300
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/infra/runners/StateHolder.java	Mon May 30 18:30:11 2016 +0300
@@ -33,32 +33,40 @@
     public final boolean stopped;
     public final P[] pairs;
     public final int countWorkers;
+    public final SpinLoopStyle spinStyle;
     public final AtomicInteger started, ready, finished;
-    public volatile boolean notAllStarted, notAllReady, notAllFinished;
+    public volatile boolean notAllStarted, notAllReady, notAllFinished, notConsumed;
     public volatile boolean hasLaggedWorkers;
 
-    public StateHolder(boolean stopped, P[] pairs, int expectedWorkers) {
+    public StateHolder(boolean stopped, P[] pairs, int expectedWorkers, SpinLoopStyle spinStyle) {
         this.stopped = stopped;
         this.pairs = pairs;
         this.countWorkers = expectedWorkers;
+        this.spinStyle = spinStyle;
         this.ready = new AtomicInteger(expectedWorkers);
         this.started = new AtomicInteger(expectedWorkers);
         this.finished = new AtomicInteger(expectedWorkers);
         this.notAllReady = true;
         this.notAllFinished = true;
         this.notAllStarted = true;
+        this.notConsumed = true;
     }
 
-    public void preRun(boolean shouldYield) {
+    public void preRun() {
         int v = ready.decrementAndGet();
         if (v == 0) {
             notAllReady = false;
         }
-        if (v < 0) {
-            throw new IllegalStateException("Oops: " + v);
-        }
-        while (notAllReady) {
-            if (shouldYield) Thread.yield();
+
+        switch (spinStyle) {
+            case THREAD_YIELD:
+                while (notAllReady) Thread.yield();
+                break;
+            case THREAD_SPIN_WAIT:
+                while (notAllReady) Thread.onSpinWait();
+                break;
+            default:
+                while (notAllReady);
         }
 
         if (started.decrementAndGet() == 0) {
@@ -66,15 +74,36 @@
         }
     }
 
-    public void postRun(boolean shouldYield) {
+    public void postRun() {
         if (finished.decrementAndGet() == 0) {
             notAllFinished = false;
         }
         hasLaggedWorkers |= notAllStarted;
 
-        while (notAllFinished) {
-            if (shouldYield) Thread.yield();
+        switch (spinStyle) {
+            case THREAD_YIELD:
+                while (notAllFinished) Thread.yield();
+                break;
+            case THREAD_SPIN_WAIT:
+                while (notAllFinished) Thread.onSpinWait();
+                break;
+            default:
+                while (notAllFinished);
         }
     }
 
+    public void postConsume() {
+        switch (spinStyle) {
+            case THREAD_YIELD:
+                while (notConsumed) Thread.yield();
+                break;
+            case THREAD_SPIN_WAIT:
+                while (notConsumed) Thread.onSpinWait();
+                break;
+            default:
+                while (notConsumed);
+        }
+
+    }
+
 }
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/infra/runners/TestConfig.java	Mon May 30 15:25:00 2016 +0300
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/infra/runners/TestConfig.java	Mon May 30 18:30:11 2016 +0300
@@ -35,7 +35,7 @@
 
     public static final Comparator<TestConfig> COMPARATOR_NAME = Comparator.comparing((c) -> c.name);
 
-    public final boolean shouldYield;
+    public final SpinLoopStyle spinLoopStyle;
     public final boolean verbose;
     public final int minStride;
     public final int maxStride;
@@ -62,7 +62,7 @@
         minStride = opts.getMinStride();
         maxStride = opts.getMaxStride();
         iters = opts.getIterations();
-        shouldYield = opts.shouldYield();
+        spinLoopStyle = opts.getSpinStyle();
         verbose = opts.isVerbose();
         deoptRatio = opts.deoptRatio();
         threads = info.threads();
@@ -77,7 +77,7 @@
 
         TestConfig that = (TestConfig) o;
 
-        if (shouldYield != that.shouldYield) return false;
+        if (spinLoopStyle != that.spinLoopStyle) return false;
         if (minStride != that.minStride) return false;
         if (maxStride != that.maxStride) return false;
         if (time != that.time) return false;
@@ -92,7 +92,7 @@
 
     @Override
     public int hashCode() {
-        int result = (shouldYield ? 1 : 0);
+        int result = spinLoopStyle.hashCode();
         result = 31 * result + minStride;
         result = 31 * result + maxStride;
         result = 31 * result + time;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/vm/ThreadSpinWaitTestMain.java	Mon May 30 18:30:11 2016 +0300
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package org.openjdk.jcstress.vm;
+
+public class ThreadSpinWaitTestMain {
+
+    public static void main(String... args) {
+        Thread.onSpinWait();
+    }
+
+}
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/vm/VMSupport.java	Mon May 30 15:25:00 2016 +0300
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/vm/VMSupport.java	Mon May 30 18:30:11 2016 +0300
@@ -34,11 +34,17 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.stream.Collectors;
 
 public class VMSupport {
 
     private static final List<String> ADD_JVM_FLAGS = new ArrayList<>();
     private static final Collection<Collection<String>> AVAIL_JVM_MODES = new ArrayList<>();
+    private static volatile boolean THREAD_SPIN_WAIT_AVAILABLE;
+
+    public static boolean spinWaitHintAvailable() {
+        return THREAD_SPIN_WAIT_AVAILABLE;
+    }
 
     public static void initSupport() {
         System.out.println("Initializing and probing the target VM: ");
@@ -64,17 +70,26 @@
                 "-XX:+WhiteBoxAPI",
                 DeoptTestMain.class);
 
+        THREAD_SPIN_WAIT_AVAILABLE =
+                detect("Trying Thread.onSpinWait",
+                "",
+                ThreadSpinWaitTestMain.class);
+
         System.out.println();
     }
 
-    private static void detect(String label, String opt, Class<?> mainClass) {
+    private static boolean detect(String label, String opt, Class<?> mainClass) {
         try {
             tryWith(opt, mainClass.getName());
-            ADD_JVM_FLAGS.add(opt);
+            if (!opt.isEmpty()) {
+                ADD_JVM_FLAGS.add(opt);
+            }
             System.out.printf("----- %s %s%n", "[OK]", label);
+            return true;
         } catch (VMSupportException ex) {
             System.out.printf("----- %s %s%n", "[FAILED]", label);
             System.out.println(ex.getMessage());
+            return false;
         }
     }
 
@@ -124,7 +139,10 @@
     public static void tryWith(String... lines) throws VMSupportException {
         try {
             List<String> commandString = getJavaInvokeLine();
-            commandString.addAll(Arrays.asList(lines));
+            commandString.addAll(
+                    Arrays.stream(lines)
+                            .filter(s -> !s.isEmpty())
+                            .collect(Collectors.toList()));
 
             ProcessBuilder pb = new ProcessBuilder(commandString);
             Process p = pb.start();