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