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