Path: blob/master/test/hotspot/jtreg/compiler/gcbarriers/UnsafeIntrinsicsTest.java
64476 views
/*1* Copyright (c) 2017, 2021, 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 8059022 827185527* @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);291UNSAFE.storeFence(); // Make all new Node fields visible to concurrent readers.292startNode.setNext(head);293return temp;294}295296private Node mergeImplSwap(Node startNode, Node expectedNext, Node head) {297// Swap version298return (Node) UNSAFE.getAndSetReference(startNode, offset, head);299}300301private Node mergeImplCAS(Node startNode, Node expectedNext, Node head) {302// CAS - should always be true within a single thread - no other thread can have overwritten303if (!UNSAFE.compareAndSetReference(startNode, offset, expectedNext, head)) {304throw new Error("CAS should always succeed on thread local objects, check your barrier implementation");305}306return expectedNext; // continue on old circle307}308309private Node mergeImplCASFail(Node startNode, Node expectedNext, Node head) {310// Force a fail311if (UNSAFE.compareAndSetReference(startNode, offset, "fail", head)) {312throw new Error("This CAS should always fail, check your barrier implementation");313}314if (startNode.next() != expectedNext) {315throw new Error("Shouldn't have changed");316}317return current;318}319320private Node mergeImplWeakCAS(Node startNode, Node expectedNext, Node head) {321// Weak CAS - should almost always be true within a single thread - no other thread can have overwritten322// Spurious failures are allowed. So, we retry a couple of times on failure.323boolean ok = false;324for (int i = 0; i < 3; ++i) {325ok = UNSAFE.weakCompareAndSetReference(startNode, offset, expectedNext, head);326if (ok) break;327}328if (!ok) {329throw new Error("Weak CAS should almost always succeed on thread local objects, check your barrier implementation");330}331return expectedNext; // continue on old circle332}333334private Node mergeImplWeakCASFail(Node startNode, Node expectedNext, Node head) {335// Force a fail336if (UNSAFE.weakCompareAndSetReference(startNode, offset, "fail", head)) {337throw new Error("This weak CAS should always fail, check your barrier implementation");338}339if (startNode.next() != expectedNext) {340throw new Error("Shouldn't have changed");341}342return current;343}344345private Node mergeImplCMPX(Node startNode, Node expectedNext, Node head) {346// CmpX - should always be true within a single thread - no other thread can have overwritten347Object res = UNSAFE.compareAndExchangeReference(startNode, offset, expectedNext, head);348if (!res.equals(expectedNext)) {349throw new Error("Fail CmpX should always succeed on thread local objects, check your barrier implementation");350}351return expectedNext; // continue on old circle352}353354private Node mergeImplCMPXFail(Node startNode, Node expectedNext, Node head) {355Object res = UNSAFE.compareAndExchangeReference(startNode, offset, head, head);356if (startNode.next() != expectedNext) {357throw new Error("Shouldn't have changed");358}359if (head == expectedNext) {360throw new Error("Test malfunction");361}362if (!res.equals(expectedNext)) {363throw new Error("This CmpX should have returned 'expectedNext' when it failed");364}365if (res.equals(head)) {366throw new Error("This CmpX shouldn't have returned head when it failed. count: "+ iterations);367}368369return current;370}371372// Create a new branch that will replace a part of the circle373public Node makeBranch(Node end_node, int count) {374Node head = end_node;375for (int i = 0; i < count; i++) {376head = new Node(head);377}378return head;379}380}381382class Node {383Node next;384int number = 0;385386public int number() {387return number;388}389390public void setNumber(int v) {391number = v;392}393394public Node() {395}396397public Node(Node link) {398next = link;399}400401public void setNext(Node next) {402this.next = next;403}404public Node next() {405return next;406}407}408409410