Path: blob/master/test/hotspot/jtreg/compiler/gcbarriers/UnsafeIntrinsicsTest.java
41149 views
/*1* Copyright (c) 2017, 2020, 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*/2223/*24* @test id=z25* @key randomness26* @bug 805902227* @modules java.base/jdk.internal.misc:+open28* @summary Validate barriers after Unsafe getReference, CAS and swap (GetAndSet)29* @requires vm.gc.Z30* @library /test/lib31* @run main/othervm -XX:+UseZGC32* -XX:+UnlockDiagnosticVMOptions33* -XX:+ZVerifyViews -XX:ZCollectionInterval=134* -XX:-CreateCoredumpOnCrash35* -XX:CompileCommand=dontinline,*::mergeImpl*36* compiler.gcbarriers.UnsafeIntrinsicsTest37*/3839/*40* @test id=shenandoah41* @key randomness42* @bug 8255401 825194443* @modules java.base/jdk.internal.misc:+open44* @summary Validate barriers after Unsafe getReference, CAS and swap (GetAndSet)45* @requires vm.gc.Shenandoah46* @library /test/lib47* @run main/othervm -XX:+UseShenandoahGC48* -XX:+UnlockDiagnosticVMOptions49* -XX:-CreateCoredumpOnCrash50* -XX:+ShenandoahVerify51* -XX:+IgnoreUnrecognizedVMOptions -XX:+ShenandoahVerifyOptoBarriers52* -XX:CompileCommand=dontinline,*::mergeImpl*53* compiler.gcbarriers.UnsafeIntrinsicsTest54*/5556package compiler.gcbarriers;5758import java.lang.reflect.Field;59import java.util.ArrayList;60import java.util.Random;61import jdk.test.lib.Utils;62import sun.misc.Unsafe;6364public class UnsafeIntrinsicsTest {6566/*67* This test triggers the loadbarriers by allocating a lot, keeping the objects alive and then68* letting them die in a way that maximizes fragmentation.69*70* All subtests (OperationType's) could run in parallel.71*/7273static int node_count = 133700;74static int thread_count = 4;75static int time = Integer.getInteger("time", 4); // seconds per subtest7677static Runner r = new Runner(null, 1, 1, Runner.OperationType.CAS);7879static Node first_node;80int epoch = 0;8182public static void main(String[] args) {83UnsafeIntrinsicsTest t = new UnsafeIntrinsicsTest();8485t.testWithLocalData(Runner.OperationType.CAS);86t.testWithLocalData(Runner.OperationType.Weak_CAS);87t.testWithLocalData(Runner.OperationType.CMPX);8889t.testWithSharedData(Runner.OperationType.Swap);90t.testWithSharedData(Runner.OperationType.Load);91}9293public UnsafeIntrinsicsTest() {9495}9697public void testWithLocalData(Runner.OperationType optype) {98System.out.println("Testing " + optype.name() + " with " + thread_count +" thread and " + node_count + " nodes");99100// start mutator threads101ArrayList<Thread> thread_list = new ArrayList<Thread>();102Random r = Utils.getRandomInstance();103for (int i = 0; i < thread_count; i++) {104105setup(); // each thread has its own circle of nodes106Thread t = new Thread(new Runner(first_node, time, r.nextLong(), optype));107t.start();108thread_list.add(t);109}110111waitForCompletion(thread_list);112countNodes();113}114115public void testWithSharedData(Runner.OperationType optype) {116System.out.println("Testing " + optype.name() + " with " + thread_count +" thread and " + node_count + " nodes");117118setup(); // All nodes are shared between threads119ArrayList<Thread> thread_list = new ArrayList<Thread>();120Random r = Utils.getRandomInstance();121for (int i = 0; i < thread_count; i++) {122Thread t = new Thread(new Runner(first_node, time, r.nextLong(), optype));123t.start();124thread_list.add(t);125}126127waitForCompletion(thread_list);128countNodes();129}130131public void waitForCompletion(ArrayList<Thread> thread_list) {132// do some waiting133try {134Thread.sleep(time*1000);135} catch (InterruptedException e) {136e.printStackTrace();137}138139// wait for all thread to terminate140for (int i = 0; i < thread_count; i++) {141try {142thread_list.get(i).join();143} catch (InterruptedException e) {144e.printStackTrace();145}146}147}148149void countNodes() {150epoch++;151int count = 0;152Node node = first_node;153while (node.number() < epoch) {154node.setNumber(epoch);155count++;156node = node.next();157}158System.out.println("Program end, found " + count + " nodes");159}160161// Create a circular linked list162public void setup() {163first_node = new Node();164Node last_node = first_node;165for (int i = 0; i < node_count; i++) {166last_node = new Node(last_node);167}168first_node.setNext(last_node);169}170}171172class Runner implements Runnable {173174OperationType type;175Node current;176Random r;177long time;178long seed;179180long milage = 0;181long created = 0;182long skipped = 0;183int iterations = 0;184185static final jdk.internal.misc.Unsafe UNSAFE;186static final long offset;187188public enum OperationType {189Load("Load"),190Swap("Swap"),191CAS("CAS"),192Weak_CAS("Weak-CAS"),193CMPX("CMPX");194195private String name;196private OperationType(String name) { this.name = name; }197}198199static {200try {201Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe");202f.setAccessible(true);203UNSAFE = (jdk.internal.misc.Unsafe) f.get(null);204offset = UNSAFE.objectFieldOffset(Node.class.getDeclaredField("next"));205} catch (Exception e) {206throw new RuntimeException("Unable to get Unsafe instance.", e);207}208}209210public Runner(Node start, int testtime, long seed, OperationType type) {211current = start;212time = testtime*1000000000L;213r = new Random(seed);214this.type = type;215}216217@Override218public void run() {219long starttime = System.nanoTime();220while((System.nanoTime() - starttime) < time) {221iterations++;222// Run a bit223int run_length = r.nextInt() & 0xfff;224for (int i = 0; i < run_length; i++) {225current = current.next();226milage++;227}228// find a start node229Node startNode = current;230Node expectedNext = startNode.next;231232// Run a bit more233int skip_length = (r.nextInt() & 0xff) + 1;234for (int i = 0; i < skip_length; i++) {235current = current.next();236skipped++;237}238239// create a branch240int branch_length = (r.nextInt() & 0xff) + 1;241created += branch_length;242Node head = makeBranch(current, branch_length);243244// complete circle, but continue to run on old path245boolean test_fail = ((iterations & 0x1) == 0);246Node current = merge(startNode, expectedNext, head, test_fail);247}248System.out.println("Milage: " + milage + " Skipped: " + skipped + " Created: " + created + " iterations: " + iterations);249}250251/*252* The reason for the duplicated code that is wrapping the unsafe operations is that we want253* to test the operations individually. They must not interfere with each other - checking a field254* will heal that reference and no operation after can trigger the barrier.255*256* All mergeImpl*-method are prevented from being inlined.257*/258259private Node merge(Node startNode, Node expectedNext, Node head, boolean test_fail) {260switch (type) {261case Load:262return mergeImplLoad(startNode, expectedNext, head);263case Swap:264return mergeImplSwap(startNode, expectedNext, head);265case CAS:266if (test_fail) {267return mergeImplCASFail(startNode, expectedNext, head);268} else {269return mergeImplCAS(startNode, expectedNext, head);270}271case Weak_CAS:272if (test_fail) {273return mergeImplWeakCASFail(startNode, expectedNext, head);274} else {275return mergeImplWeakCAS(startNode, expectedNext, head);276}277case CMPX:278if (test_fail) {279return mergeImplCMPXFail(startNode, expectedNext, head);280} else {281return mergeImplCMPX(startNode, expectedNext, head);282}283default:284throw new Error("Unimplemented");285}286}287288private Node mergeImplLoad(Node startNode, Node expectedNext, Node head) {289// Atomic load version290Node temp = (Node) UNSAFE.getReference(startNode, offset);291startNode.setNext(head);292return temp;293}294295private Node mergeImplSwap(Node startNode, Node expectedNext, Node head) {296// Swap version297return (Node) UNSAFE.getAndSetReference(startNode, offset, head);298}299300private Node mergeImplCAS(Node startNode, Node expectedNext, Node head) {301// CAS - should always be true within a single thread - no other thread can have overwritten302if (!UNSAFE.compareAndSetReference(startNode, offset, expectedNext, head)) {303throw new Error("CAS should always succeed on thread local objects, check you barrier implementation");304}305return expectedNext; // continue on old circle306}307308private Node mergeImplCASFail(Node startNode, Node expectedNext, Node head) {309// Force a fail310if (UNSAFE.compareAndSetReference(startNode, offset, "fail", head)) {311throw new Error("This CAS should always fail, check you barrier implementation");312}313if (startNode.next() != expectedNext) {314throw new Error("Shouldn't have changed");315}316return current;317}318319private Node mergeImplWeakCAS(Node startNode, Node expectedNext, Node head) {320// Weak CAS - should always be true within a single thread - no other thread can have overwritten321if (!UNSAFE.weakCompareAndSetReference(startNode, offset, expectedNext, head)) {322throw new Error("Weak CAS should always succeed on thread local objects, check you barrier implementation");323}324return expectedNext; // continue on old circle325}326327private Node mergeImplWeakCASFail(Node startNode, Node expectedNext, Node head) {328// Force a fail329if (UNSAFE.weakCompareAndSetReference(startNode, offset, "fail", head)) {330throw new Error("This weak CAS should always fail, check you barrier implementation");331}332if (startNode.next() != expectedNext) {333throw new Error("Shouldn't have changed");334}335return current;336}337338private Node mergeImplCMPX(Node startNode, Node expectedNext, Node head) {339// CmpX - should always be true within a single thread - no other thread can have overwritten340Object res = UNSAFE.compareAndExchangeReference(startNode, offset, expectedNext, head);341if (!res.equals(expectedNext)) {342throw new Error("Fail CmpX should always succeed on thread local objects, check you barrier implementation");343}344return expectedNext; // continue on old circle345}346347private Node mergeImplCMPXFail(Node startNode, Node expectedNext, Node head) {348Object res = UNSAFE.compareAndExchangeReference(startNode, offset, head, head);349if (startNode.next() != expectedNext) {350throw new Error("Shouldn't have changed");351}352if (head == expectedNext) {353throw new Error("Test malfunction");354}355if (!res.equals(expectedNext)) {356throw new Error("This CmpX should have returned 'expectedNext' when it failed");357}358if (res.equals(head)) {359throw new Error("This CmpX shouldn't have returned head when it failed. count: "+ iterations);360}361362return current;363}364365// Create a new branch that will replace a part of the circle366public Node makeBranch(Node end_node, int count) {367Node head = end_node;368for (int i = 0; i < count; i++) {369head = new Node(head);370}371return head;372}373}374375class Node {376Node next;377int number = 0;378379public int number() {380return number;381}382383public void setNumber(int v) {384number = v;385}386387public Node() {388}389390public Node(Node link) {391next = link;392}393394public void setNext(Node next) {395this.next = next;396}397public Node next() {398return next;399}400}401402403