1 /*
   2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package vm.mlvm.share;
  25 
  26 import java.util.concurrent.BrokenBarrierException;
  27 import java.util.concurrent.CyclicBarrier;
  28 import nsk.share.Log.TraceLevel;
  29 import vm.share.options.Option;
  30 
  31 /**
  32  * The test looks for late CPU stores, which is not visible for other CPU.
  33  * <p>
  34  * In absence of synchronization (such as memory barriers and so on), typical modern CPUs
  35  * (Intel x86 32/64, SPARC in TSO mode) put stores into buffer instead of immediately writing them to memory.
  36  *
  37  * So the following program:
  38  * <code>
  39  *    write A
  40  *    read B
  41  * </code>
  42  * can actually be transformed to and seen by other processors as:
  43  * <code>
  44  *    read B
  45  *    write A
  46  *  </code>
  47  * <p>
  48  * DekkerTest runs two threads, A and B, which perform operations on a shared array of Actors concurrently.
  49  * Reordering mentioned above is detected by the test and reported as failure
  50  * (unless mayFail option is specified in the constructor or command-line option).
  51  *
  52  * <p>
  53  * The tests should subclass Actor with test-specific data and code.
  54  * The Actor subclass requirements:
  55  *
  56  * <ul>
  57  * <li>the class should have two data fields:
  58  * <code>
  59  *   A
  60  *   B
  61  * </code>
  62  *   of the same type, which are able hold one of two values.
  63  *   Let's call these values TRUE and FALSE (these can be any two values, say 0 and 1).
  64  *
  65  * <li>* actorA() is called from thread A and should implement the following pseudo-code:
  66  * <code>
  67  *   A = TRUE
  68  *   optional MEMBAR #StoreLoad
  69  *   return (B == TRUE)
  70  * </code>
  71  *
  72  * <li>actorB() is called from thread B and should do the following:
  73  * <code>
  74  *   {
  75  *       B = TRUE
  76  *       optional MEMBAR #StoreLoad
  77  *       return (A == TRUE)
  78  *   }
  79  * </code>
  80  *
  81  * <li>reset() method should do the following:
  82  * <code>
  83  *   {
  84  *       A = FALSE
  85  *       B = FALSE
  86  *   }
  87  * </code>
  88  *
  89  *   The use of memory barriers in actorX() methods is up to the implementor -- depending on the goal of testing.
  90  *
  91  */
  92 public class DekkerTest extends MlvmTest {
  93 
  94     @Option(name = "actorClass", default_value = "", description = "Name of actor class (see test comments)")
  95     private String actorClassNameOpt = "";
  96 
  97     @Option(name = "mayFail", default_value = "false", description = "Don't report test failure (use it just a stress test)")
  98     private boolean mayFailOpt;
  99 
 100     @Option(name = "iterations", default_value = "1000000", description = "Number of iterations in each Actor thread (i.e., shared array size)")
 101     private int iterationsOpt = 1000000;
 102 
 103     /**
 104      * Actor interface, which should be implemented for using with DekkerTest.
 105      * The requirements for Actor implementation are outlined in {@link DekkerTest} class documentation.
 106      */
 107     public interface Actor {
 108         /**
 109          * Sets fields A and B to false
 110          */
 111         void reset();
 112 
 113         /**
 114          * Sets field A to true, and returns field B contents
 115          * @return field B contents
 116          * @throws Throwable in case of error
 117          */
 118         boolean actorA() throws Throwable;
 119 
 120         /**
 121          * Sets field B to true, and returns field A contents
 122          * @return field A contents
 123          * @throws Throwable in case of error
 124          */
 125         boolean actorB() throws Throwable;
 126     }
 127 
 128     private static final class ResultData {
 129         public boolean a;
 130         public boolean b;
 131 
 132         public ResultData() {
 133         }
 134 
 135         public void reset() {
 136             a = false;
 137             b = false;
 138         }
 139     }
 140 
 141     private static class RaceStatistics {
 142         public int syncErrors;
 143         public int runSideBySide;
 144         public int A_outruns_B;
 145         public int B_outruns_A;
 146 
 147         public void reset() {
 148             syncErrors = 0;
 149             runSideBySide = 0;
 150             A_outruns_B = 0;
 151             B_outruns_A = 0;
 152         }
 153     }
 154 
 155     private Class<? extends Actor> actorClass;
 156     private final CyclicBarrier startBarrier = new CyclicBarrier(2); // We have two actors
 157     private Actor[] testData;
 158     private ResultData[] results;
 159     private final RaceStatistics raceStatistics = new RaceStatistics();
 160 
 161     public DekkerTest() {
 162     }
 163 
 164     /**
 165      * Sets actor class.
 166      * @param ac Actor class, which implements DekkerTest.Actor interface
 167      */
 168     public void setActorClass(Class<? extends Actor> ac) {
 169         actorClass = ac;
 170     }
 171 
 172     /**
 173      * Sets mayFail option. When set to true, synchronization failure is not reported as test failure (DekkerTest is used as a stress test).
 174      * @param mayFail if true, don't report sync failure as test failure, false otherwise
 175      */
 176     public void setMayFailOpt(boolean mayFail) {
 177         mayFailOpt = mayFail;
 178     }
 179 
 180     /**
 181      * Initializes test data, parses command-line options and checks Actor class
 182      */
 183     @Override
 184     public void initializeTest() {
 185         // Override values set by setActorClass() by command-line option, if any
 186         try {
 187             if (!actorClassNameOpt.isEmpty()) {
 188                 actorClass = Class.forName(actorClassNameOpt).asSubclass(Actor.class);
 189             } else {
 190                 if (actorClass == null) {
 191                     throw new RuntimeException("Test error: the actor class should be specified via command-line options or in the constructor");
 192                 }
 193             }
 194 
 195         } catch (ClassNotFoundException e) {
 196             throw new RuntimeException("Actor class '" + actorClassNameOpt + "' not found", e);
 197         } catch (ClassCastException e) {
 198             throw new RuntimeException("Test error: the actor class " + actorClass.getName() + " should implement " + Actor.class.getName() + " interface", e);
 199         }
 200 
 201         // Generate data
 202         int iterations = iterationsOpt * getStressOptions().getIterationsFactor();
 203         if (iterations <= 0) {
 204             throw new RuntimeException("Invalid number of iterations specified in -iterations and -stressIterationsFactor options: " + iterations);
 205         }
 206 
 207         results = new ResultData[iterations];
 208         for (int i = 0; i < results.length; ++i) {
 209             results[i] = new ResultData();
 210         }
 211 
 212         testData = new Actor[results.length];
 213         try {
 214             for (int i = 0; i < testData.length; ++i) {
 215                 testData[i] = actorClass.newInstance();
 216             }
 217         } catch (ReflectiveOperationException e) {
 218             throw new RuntimeException("Test error: can't instantiate class " + actorClass.getName(), e);
 219         }
 220     }
 221 
 222     /**
 223      * Resets the test data between test runs
 224      */
 225     @Override
 226     public void resetTest() {
 227         for (Actor a : testData) {
 228             a.reset();
 229         }
 230         for (ResultData d : results) {
 231             d.reset();
 232         }
 233     }
 234 
 235     private class RunnerA extends Thread {
 236         @Override
 237         public void run() {
 238             try {
 239                 startBarrier.await();
 240                 for (int i = 0; i < results.length; ++i) {
 241                     results[i].a = testData[i].actorA();
 242                 }
 243             } catch (Throwable t) {
 244                 markTestFailed("Exception in RunnerA", t);
 245             }
 246         }
 247     }
 248 
 249     private class RunnerB extends Thread {
 250         @Override
 251         public void run() {
 252             try {
 253                 startBarrier.await();
 254                 for (int i = 0; i < results.length; ++i) {
 255                     results[i].b = testData[i].actorB();
 256                 }
 257             } catch (Throwable t) {
 258                 markTestFailed("Exception in RunnerB", t);
 259             }
 260         }
 261     }
 262 
 263     @Override
 264     public boolean run() throws Throwable {
 265         RunnerA threadA = new RunnerA();
 266         threadA.start();
 267 
 268         RunnerB threadB = new RunnerB();
 269         threadB.start();
 270 
 271         threadA.join();
 272         threadB.join();
 273 
 274         if (isMarkedFailed())
 275             return false;
 276 
 277         analyzeResults();
 278         printResults();
 279 
 280         if (mayFailOpt) {
 281             return true;
 282         }
 283 
 284         return (raceStatistics.syncErrors == 0);
 285     }
 286 
 287     private void analyzeResults() {
 288         raceStatistics.reset();
 289 
 290         for (int i = 0; i < results.length; ++i) {
 291             boolean resultA = results[i].a;
 292             boolean resultB = results[i].b;
 293 
 294             if (resultA && resultB) {
 295 
 296                 ++raceStatistics.runSideBySide;
 297 
 298             } else if (resultA && !resultB) {
 299 
 300                 ++raceStatistics.A_outruns_B;
 301 
 302             } else if (!resultA && resultB) {
 303 
 304                 ++raceStatistics.B_outruns_A;
 305 
 306             } else if (!resultA && !resultB) {
 307 
 308                 ++raceStatistics.syncErrors;
 309 
 310             } else {
 311 
 312                 throw new RuntimeException("Should not reach here");
 313             }
 314         }
 315     }
 316 
 317     private void printResults() {
 318         int logLevel = (raceStatistics.syncErrors != 0) ? TraceLevel.TRACE_IMPORTANT : TraceLevel.TRACE_NORMAL;
 319 
 320         Env.getLog().trace(logLevel, "\n"
 321                 + "Late stores (sync. errors): " + raceStatistics.syncErrors + "\n"
 322                 + "B outruns A               : " + raceStatistics.B_outruns_A + "\n"
 323                 + "A outruns B               : " + raceStatistics.A_outruns_B + "\n"
 324                 + "A and B run side by side  : " + raceStatistics.runSideBySide);
 325     }
 326 
 327     public static void main(String[] args) {
 328         MlvmTest.launch(args);
 329     }
 330 }
 331