Path: blob/master/test/hotspot/jtreg/vmTestbase/vm/mlvm/share/DekkerTest.java
41155 views
/*1* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.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 it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 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 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package vm.mlvm.share;2425import java.util.concurrent.BrokenBarrierException;26import java.util.concurrent.CyclicBarrier;27import nsk.share.Log.TraceLevel;28import vm.share.options.Option;2930/**31* The test looks for late CPU stores, which is not visible for other CPU.32* <p>33* In absence of synchronization (such as memory barriers and so on), typical modern CPUs34* (Intel x86 32/64, SPARC in TSO mode) put stores into buffer instead of immediately writing them to memory.35*36* So the following program:37* <code>38* write A39* read B40* </code>41* can actually be transformed to and seen by other processors as:42* <code>43* read B44* write A45* </code>46* <p>47* DekkerTest runs two threads, A and B, which perform operations on a shared array of Actors concurrently.48* Reordering mentioned above is detected by the test and reported as failure49* (unless mayFail option is specified in the constructor or command-line option).50*51* <p>52* The tests should subclass Actor with test-specific data and code.53* The Actor subclass requirements:54*55* <ul>56* <li>the class should have two data fields:57* <code>58* A59* B60* </code>61* of the same type, which are able hold one of two values.62* Let's call these values TRUE and FALSE (these can be any two values, say 0 and 1).63*64* <li>* actorA() is called from thread A and should implement the following pseudo-code:65* <code>66* A = TRUE67* optional MEMBAR #StoreLoad68* return (B == TRUE)69* </code>70*71* <li>actorB() is called from thread B and should do the following:72* <code>73* {74* B = TRUE75* optional MEMBAR #StoreLoad76* return (A == TRUE)77* }78* </code>79*80* <li>reset() method should do the following:81* <code>82* {83* A = FALSE84* B = FALSE85* }86* </code>87*88* The use of memory barriers in actorX() methods is up to the implementor -- depending on the goal of testing.89*90*/91public class DekkerTest extends MlvmTest {9293@Option(name = "actorClass", default_value = "", description = "Name of actor class (see test comments)")94private String actorClassNameOpt = "";9596@Option(name = "mayFail", default_value = "false", description = "Don't report test failure (use it just a stress test)")97private boolean mayFailOpt;9899@Option(name = "iterations", default_value = "1000000", description = "Number of iterations in each Actor thread (i.e., shared array size)")100private int iterationsOpt = 1000000;101102/**103* Actor interface, which should be implemented for using with DekkerTest.104* The requirements for Actor implementation are outlined in {@link DekkerTest} class documentation.105*/106public interface Actor {107/**108* Sets fields A and B to false109*/110void reset();111112/**113* Sets field A to true, and returns field B contents114* @return field B contents115* @throws Throwable in case of error116*/117boolean actorA() throws Throwable;118119/**120* Sets field B to true, and returns field A contents121* @return field A contents122* @throws Throwable in case of error123*/124boolean actorB() throws Throwable;125}126127private static final class ResultData {128public boolean a;129public boolean b;130131public ResultData() {132}133134public void reset() {135a = false;136b = false;137}138}139140private static class RaceStatistics {141public int syncErrors;142public int runSideBySide;143public int A_outruns_B;144public int B_outruns_A;145146public void reset() {147syncErrors = 0;148runSideBySide = 0;149A_outruns_B = 0;150B_outruns_A = 0;151}152}153154private Class<? extends Actor> actorClass;155private final CyclicBarrier startBarrier = new CyclicBarrier(2); // We have two actors156private Actor[] testData;157private ResultData[] results;158private final RaceStatistics raceStatistics = new RaceStatistics();159160public DekkerTest() {161}162163/**164* Sets actor class.165* @param ac Actor class, which implements DekkerTest.Actor interface166*/167public void setActorClass(Class<? extends Actor> ac) {168actorClass = ac;169}170171/**172* Sets mayFail option. When set to true, synchronization failure is not reported as test failure (DekkerTest is used as a stress test).173* @param mayFail if true, don't report sync failure as test failure, false otherwise174*/175public void setMayFailOpt(boolean mayFail) {176mayFailOpt = mayFail;177}178179/**180* Initializes test data, parses command-line options and checks Actor class181*/182@Override183public void initializeTest() {184// Override values set by setActorClass() by command-line option, if any185try {186if (!actorClassNameOpt.isEmpty()) {187actorClass = Class.forName(actorClassNameOpt).asSubclass(Actor.class);188} else {189if (actorClass == null) {190throw new RuntimeException("Test error: the actor class should be specified via command-line options or in the constructor");191}192}193194} catch (ClassNotFoundException e) {195throw new RuntimeException("Actor class '" + actorClassNameOpt + "' not found", e);196} catch (ClassCastException e) {197throw new RuntimeException("Test error: the actor class " + actorClass.getName() + " should implement " + Actor.class.getName() + " interface", e);198}199200// Generate data201int iterations = iterationsOpt * getStressOptions().getIterationsFactor();202if (iterations <= 0) {203throw new RuntimeException("Invalid number of iterations specified in -iterations and -stressIterationsFactor options: " + iterations);204}205206results = new ResultData[iterations];207for (int i = 0; i < results.length; ++i) {208results[i] = new ResultData();209}210211testData = new Actor[results.length];212try {213for (int i = 0; i < testData.length; ++i) {214testData[i] = actorClass.newInstance();215}216} catch (ReflectiveOperationException e) {217throw new RuntimeException("Test error: can't instantiate class " + actorClass.getName(), e);218}219}220221/**222* Resets the test data between test runs223*/224@Override225public void resetTest() {226for (Actor a : testData) {227a.reset();228}229for (ResultData d : results) {230d.reset();231}232}233234private class RunnerA extends Thread {235@Override236public void run() {237try {238startBarrier.await();239for (int i = 0; i < results.length; ++i) {240results[i].a = testData[i].actorA();241}242} catch (Throwable t) {243markTestFailed("Exception in RunnerA", t);244}245}246}247248private class RunnerB extends Thread {249@Override250public void run() {251try {252startBarrier.await();253for (int i = 0; i < results.length; ++i) {254results[i].b = testData[i].actorB();255}256} catch (Throwable t) {257markTestFailed("Exception in RunnerB", t);258}259}260}261262@Override263public boolean run() throws Throwable {264RunnerA threadA = new RunnerA();265threadA.start();266267RunnerB threadB = new RunnerB();268threadB.start();269270threadA.join();271threadB.join();272273if (isMarkedFailed())274return false;275276analyzeResults();277printResults();278279if (mayFailOpt) {280return true;281}282283return (raceStatistics.syncErrors == 0);284}285286private void analyzeResults() {287raceStatistics.reset();288289for (int i = 0; i < results.length; ++i) {290boolean resultA = results[i].a;291boolean resultB = results[i].b;292293if (resultA && resultB) {294295++raceStatistics.runSideBySide;296297} else if (resultA && !resultB) {298299++raceStatistics.A_outruns_B;300301} else if (!resultA && resultB) {302303++raceStatistics.B_outruns_A;304305} else if (!resultA && !resultB) {306307++raceStatistics.syncErrors;308309} else {310311throw new RuntimeException("Should not reach here");312}313}314}315316private void printResults() {317int logLevel = (raceStatistics.syncErrors != 0) ? TraceLevel.TRACE_IMPORTANT : TraceLevel.TRACE_NORMAL;318319Env.getLog().trace(logLevel, "\n"320+ "Late stores (sync. errors): " + raceStatistics.syncErrors + "\n"321+ "B outruns A : " + raceStatistics.B_outruns_A + "\n"322+ "A outruns B : " + raceStatistics.A_outruns_B + "\n"323+ "A and B run side by side : " + raceStatistics.runSideBySide);324}325326public static void main(String[] args) {327MlvmTest.launch(args);328}329}330331332333