1 /* 2 * Copyright (c) 2017, 2020, 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 /* 25 * @test id=z 26 * @key randomness 27 * @bug 8059022 28 * @modules java.base/jdk.internal.misc:+open 29 * @summary Validate barriers after Unsafe getReference, CAS and swap (GetAndSet) 30 * @requires vm.gc.Z 31 * @library /test/lib 32 * @run main/othervm -XX:+UseZGC 33 * -XX:+UnlockDiagnosticVMOptions 34 * -XX:+ZVerifyViews -XX:ZCollectionInterval=1 35 * -XX:-CreateCoredumpOnCrash 36 * -XX:CompileCommand=dontinline,*::mergeImpl* 37 * compiler.gcbarriers.UnsafeIntrinsicsTest 38 */ 39 40 /* 41 * @test id=shenandoah 42 * @key randomness 43 * @bug 8255401 8251944 44 * @modules java.base/jdk.internal.misc:+open 45 * @summary Validate barriers after Unsafe getReference, CAS and swap (GetAndSet) 46 * @requires vm.gc.Shenandoah 47 * @library /test/lib 48 * @run main/othervm -XX:+UseShenandoahGC 49 * -XX:+UnlockDiagnosticVMOptions 50 * -XX:-CreateCoredumpOnCrash 51 * -XX:+ShenandoahVerify 52 * -XX:+IgnoreUnrecognizedVMOptions -XX:+ShenandoahVerifyOptoBarriers 53 * -XX:CompileCommand=dontinline,*::mergeImpl* 54 * compiler.gcbarriers.UnsafeIntrinsicsTest 55 */ 56 57 package compiler.gcbarriers; 58 59 import java.lang.reflect.Field; 60 import java.util.ArrayList; 61 import java.util.Random; 62 import jdk.test.lib.Utils; 63 import sun.misc.Unsafe; 64 65 public class UnsafeIntrinsicsTest { 66 67 /* 68 * This test triggers the loadbarriers by allocating a lot, keeping the objects alive and then 69 * letting them die in a way that maximizes fragmentation. 70 * 71 * All subtests (OperationType's) could run in parallel. 72 */ 73 74 static int node_count = 133700; 75 static int thread_count = 4; 76 static int time = Integer.getInteger("time", 4); // seconds per subtest 77 78 static Runner r = new Runner(null, 1, 1, Runner.OperationType.CAS); 79 80 static Node first_node; 81 int epoch = 0; 82 main(String[] args)83 public static void main(String[] args) { 84 UnsafeIntrinsicsTest t = new UnsafeIntrinsicsTest(); 85 86 t.testWithLocalData(Runner.OperationType.CAS); 87 t.testWithLocalData(Runner.OperationType.Weak_CAS); 88 t.testWithLocalData(Runner.OperationType.CMPX); 89 90 t.testWithSharedData(Runner.OperationType.Swap); 91 t.testWithSharedData(Runner.OperationType.Load); 92 } 93 UnsafeIntrinsicsTest()94 public UnsafeIntrinsicsTest() { 95 96 } 97 testWithLocalData(Runner.OperationType optype)98 public void testWithLocalData(Runner.OperationType optype) { 99 System.out.println("Testing " + optype.name() + " with " + thread_count +" thread and " + node_count + " nodes"); 100 101 // start mutator threads 102 ArrayList<Thread> thread_list = new ArrayList<Thread>(); 103 Random r = Utils.getRandomInstance(); 104 for (int i = 0; i < thread_count; i++) { 105 106 setup(); // each thread has its own circle of nodes 107 Thread t = new Thread(new Runner(first_node, time, r.nextLong(), optype)); 108 t.start(); 109 thread_list.add(t); 110 } 111 112 waitForCompletion(thread_list); 113 countNodes(); 114 } 115 testWithSharedData(Runner.OperationType optype)116 public void testWithSharedData(Runner.OperationType optype) { 117 System.out.println("Testing " + optype.name() + " with " + thread_count +" thread and " + node_count + " nodes"); 118 119 setup(); // All nodes are shared between threads 120 ArrayList<Thread> thread_list = new ArrayList<Thread>(); 121 Random r = Utils.getRandomInstance(); 122 for (int i = 0; i < thread_count; i++) { 123 Thread t = new Thread(new Runner(first_node, time, r.nextLong(), optype)); 124 t.start(); 125 thread_list.add(t); 126 } 127 128 waitForCompletion(thread_list); 129 countNodes(); 130 } 131 waitForCompletion(ArrayList<Thread> thread_list)132 public void waitForCompletion(ArrayList<Thread> thread_list) { 133 // do some waiting 134 try { 135 Thread.sleep(time*1000); 136 } catch (InterruptedException e) { 137 e.printStackTrace(); 138 } 139 140 // wait for all thread to terminate 141 for (int i = 0; i < thread_count; i++) { 142 try { 143 thread_list.get(i).join(); 144 } catch (InterruptedException e) { 145 e.printStackTrace(); 146 } 147 } 148 } 149 countNodes()150 void countNodes() { 151 epoch++; 152 int count = 0; 153 Node node = first_node; 154 while (node.number() < epoch) { 155 node.setNumber(epoch); 156 count++; 157 node = node.next(); 158 } 159 System.out.println("Program end, found " + count + " nodes"); 160 } 161 162 // Create a circular linked list setup()163 public void setup() { 164 first_node = new Node(); 165 Node last_node = first_node; 166 for (int i = 0; i < node_count; i++) { 167 last_node = new Node(last_node); 168 } 169 first_node.setNext(last_node); 170 } 171 } 172 173 class Runner implements Runnable { 174 175 OperationType type; 176 Node current; 177 Random r; 178 long time; 179 long seed; 180 181 long milage = 0; 182 long created = 0; 183 long skipped = 0; 184 int iterations = 0; 185 186 static final jdk.internal.misc.Unsafe UNSAFE; 187 static final long offset; 188 189 public enum OperationType { 190 Load("Load"), 191 Swap("Swap"), 192 CAS("CAS"), 193 Weak_CAS("Weak-CAS"), 194 CMPX("CMPX"); 195 196 private String name; OperationType(String name)197 private OperationType(String name) { this.name = name; } 198 } 199 200 static { 201 try { 202 Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe"); 203 f.setAccessible(true); 204 UNSAFE = (jdk.internal.misc.Unsafe) f.get(null); 205 offset = UNSAFE.objectFieldOffset(Node.class.getDeclaredField("next")); 206 } catch (Exception e) { 207 throw new RuntimeException("Unable to get Unsafe instance.", e); 208 } 209 } 210 Runner(Node start, int testtime, long seed, OperationType type)211 public Runner(Node start, int testtime, long seed, OperationType type) { 212 current = start; 213 time = testtime*1000000000L; 214 r = new Random(seed); 215 this.type = type; 216 } 217 218 @Override run()219 public void run() { 220 long starttime = System.nanoTime(); 221 while((System.nanoTime() - starttime) < time) { 222 iterations++; 223 // Run a bit 224 int run_length = r.nextInt() & 0xfff; 225 for (int i = 0; i < run_length; i++) { 226 current = current.next(); 227 milage++; 228 } 229 // find a start node 230 Node startNode = current; 231 Node expectedNext = startNode.next; 232 233 // Run a bit more 234 int skip_length = (r.nextInt() & 0xff) + 1; 235 for (int i = 0; i < skip_length; i++) { 236 current = current.next(); 237 skipped++; 238 } 239 240 // create a branch 241 int branch_length = (r.nextInt() & 0xff) + 1; 242 created += branch_length; 243 Node head = makeBranch(current, branch_length); 244 245 // complete circle, but continue to run on old path 246 boolean test_fail = ((iterations & 0x1) == 0); 247 Node current = merge(startNode, expectedNext, head, test_fail); 248 } 249 System.out.println("Milage: " + milage + " Skipped: " + skipped + " Created: " + created + " iterations: " + iterations); 250 } 251 252 /* 253 * The reason for the duplicated code that is wrapping the unsafe operations is that we want 254 * to test the operations individually. They must not interfere with each other - checking a field 255 * will heal that reference and no operation after can trigger the barrier. 256 * 257 * All mergeImpl*-method are prevented from being inlined. 258 */ 259 merge(Node startNode, Node expectedNext, Node head, boolean test_fail)260 private Node merge(Node startNode, Node expectedNext, Node head, boolean test_fail) { 261 switch (type) { 262 case Load: 263 return mergeImplLoad(startNode, expectedNext, head); 264 case Swap: 265 return mergeImplSwap(startNode, expectedNext, head); 266 case CAS: 267 if (test_fail) { 268 return mergeImplCASFail(startNode, expectedNext, head); 269 } else { 270 return mergeImplCAS(startNode, expectedNext, head); 271 } 272 case Weak_CAS: 273 if (test_fail) { 274 return mergeImplWeakCASFail(startNode, expectedNext, head); 275 } else { 276 return mergeImplWeakCAS(startNode, expectedNext, head); 277 } 278 case CMPX: 279 if (test_fail) { 280 return mergeImplCMPXFail(startNode, expectedNext, head); 281 } else { 282 return mergeImplCMPX(startNode, expectedNext, head); 283 } 284 default: 285 throw new Error("Unimplemented"); 286 } 287 } 288 mergeImplLoad(Node startNode, Node expectedNext, Node head)289 private Node mergeImplLoad(Node startNode, Node expectedNext, Node head) { 290 // Atomic load version 291 Node temp = (Node) UNSAFE.getReference(startNode, offset); 292 startNode.setNext(head); 293 return temp; 294 } 295 mergeImplSwap(Node startNode, Node expectedNext, Node head)296 private Node mergeImplSwap(Node startNode, Node expectedNext, Node head) { 297 // Swap version 298 return (Node) UNSAFE.getAndSetReference(startNode, offset, head); 299 } 300 mergeImplCAS(Node startNode, Node expectedNext, Node head)301 private Node mergeImplCAS(Node startNode, Node expectedNext, Node head) { 302 // CAS - should always be true within a single thread - no other thread can have overwritten 303 if (!UNSAFE.compareAndSetReference(startNode, offset, expectedNext, head)) { 304 throw new Error("CAS should always succeed on thread local objects, check you barrier implementation"); 305 } 306 return expectedNext; // continue on old circle 307 } 308 mergeImplCASFail(Node startNode, Node expectedNext, Node head)309 private Node mergeImplCASFail(Node startNode, Node expectedNext, Node head) { 310 // Force a fail 311 if (UNSAFE.compareAndSetReference(startNode, offset, "fail", head)) { 312 throw new Error("This CAS should always fail, check you barrier implementation"); 313 } 314 if (startNode.next() != expectedNext) { 315 throw new Error("Shouldn't have changed"); 316 } 317 return current; 318 } 319 mergeImplWeakCAS(Node startNode, Node expectedNext, Node head)320 private Node mergeImplWeakCAS(Node startNode, Node expectedNext, Node head) { 321 // Weak CAS - should always be true within a single thread - no other thread can have overwritten 322 if (!UNSAFE.weakCompareAndSetReference(startNode, offset, expectedNext, head)) { 323 throw new Error("Weak CAS should always succeed on thread local objects, check you barrier implementation"); 324 } 325 return expectedNext; // continue on old circle 326 } 327 mergeImplWeakCASFail(Node startNode, Node expectedNext, Node head)328 private Node mergeImplWeakCASFail(Node startNode, Node expectedNext, Node head) { 329 // Force a fail 330 if (UNSAFE.weakCompareAndSetReference(startNode, offset, "fail", head)) { 331 throw new Error("This weak CAS should always fail, check you barrier implementation"); 332 } 333 if (startNode.next() != expectedNext) { 334 throw new Error("Shouldn't have changed"); 335 } 336 return current; 337 } 338 mergeImplCMPX(Node startNode, Node expectedNext, Node head)339 private Node mergeImplCMPX(Node startNode, Node expectedNext, Node head) { 340 // CmpX - should always be true within a single thread - no other thread can have overwritten 341 Object res = UNSAFE.compareAndExchangeReference(startNode, offset, expectedNext, head); 342 if (!res.equals(expectedNext)) { 343 throw new Error("Fail CmpX should always succeed on thread local objects, check you barrier implementation"); 344 } 345 return expectedNext; // continue on old circle 346 } 347 mergeImplCMPXFail(Node startNode, Node expectedNext, Node head)348 private Node mergeImplCMPXFail(Node startNode, Node expectedNext, Node head) { 349 Object res = UNSAFE.compareAndExchangeReference(startNode, offset, head, head); 350 if (startNode.next() != expectedNext) { 351 throw new Error("Shouldn't have changed"); 352 } 353 if (head == expectedNext) { 354 throw new Error("Test malfunction"); 355 } 356 if (!res.equals(expectedNext)) { 357 throw new Error("This CmpX should have returned 'expectedNext' when it failed"); 358 } 359 if (res.equals(head)) { 360 throw new Error("This CmpX shouldn't have returned head when it failed. count: "+ iterations); 361 } 362 363 return current; 364 } 365 366 // Create a new branch that will replace a part of the circle makeBranch(Node end_node, int count)367 public Node makeBranch(Node end_node, int count) { 368 Node head = end_node; 369 for (int i = 0; i < count; i++) { 370 head = new Node(head); 371 } 372 return head; 373 } 374 } 375 376 class Node { 377 Node next; 378 int number = 0; 379 number()380 public int number() { 381 return number; 382 } 383 setNumber(int v)384 public void setNumber(int v) { 385 number = v; 386 } 387 Node()388 public Node() { 389 } 390 Node(Node link)391 public Node(Node link) { 392 next = link; 393 } 394 setNext(Node next)395 public void setNext(Node next) { 396 this.next = next; 397 } next()398 public Node next() { 399 return next; 400 } 401 } 402