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