Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/test/hotspot/jtreg/compiler/gcbarriers/UnsafeIntrinsicsTest.java
64476 views
1
/*
2
* Copyright (c) 2017, 2021, 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 8271855
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
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
94
public UnsafeIntrinsicsTest() {
95
96
}
97
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
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
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
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
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;
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
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
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
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
289
private Node mergeImplLoad(Node startNode, Node expectedNext, Node head) {
290
// Atomic load version
291
Node temp = (Node) UNSAFE.getReference(startNode, offset);
292
UNSAFE.storeFence(); // Make all new Node fields visible to concurrent readers.
293
startNode.setNext(head);
294
return temp;
295
}
296
297
private Node mergeImplSwap(Node startNode, Node expectedNext, Node head) {
298
// Swap version
299
return (Node) UNSAFE.getAndSetReference(startNode, offset, head);
300
}
301
302
private Node mergeImplCAS(Node startNode, Node expectedNext, Node head) {
303
// CAS - should always be true within a single thread - no other thread can have overwritten
304
if (!UNSAFE.compareAndSetReference(startNode, offset, expectedNext, head)) {
305
throw new Error("CAS should always succeed on thread local objects, check your barrier implementation");
306
}
307
return expectedNext; // continue on old circle
308
}
309
310
private Node mergeImplCASFail(Node startNode, Node expectedNext, Node head) {
311
// Force a fail
312
if (UNSAFE.compareAndSetReference(startNode, offset, "fail", head)) {
313
throw new Error("This CAS should always fail, check your barrier implementation");
314
}
315
if (startNode.next() != expectedNext) {
316
throw new Error("Shouldn't have changed");
317
}
318
return current;
319
}
320
321
private Node mergeImplWeakCAS(Node startNode, Node expectedNext, Node head) {
322
// Weak CAS - should almost always be true within a single thread - no other thread can have overwritten
323
// Spurious failures are allowed. So, we retry a couple of times on failure.
324
boolean ok = false;
325
for (int i = 0; i < 3; ++i) {
326
ok = UNSAFE.weakCompareAndSetReference(startNode, offset, expectedNext, head);
327
if (ok) break;
328
}
329
if (!ok) {
330
throw new Error("Weak CAS should almost always succeed on thread local objects, check your barrier implementation");
331
}
332
return expectedNext; // continue on old circle
333
}
334
335
private Node mergeImplWeakCASFail(Node startNode, Node expectedNext, Node head) {
336
// Force a fail
337
if (UNSAFE.weakCompareAndSetReference(startNode, offset, "fail", head)) {
338
throw new Error("This weak CAS should always fail, check your barrier implementation");
339
}
340
if (startNode.next() != expectedNext) {
341
throw new Error("Shouldn't have changed");
342
}
343
return current;
344
}
345
346
private Node mergeImplCMPX(Node startNode, Node expectedNext, Node head) {
347
// CmpX - should always be true within a single thread - no other thread can have overwritten
348
Object res = UNSAFE.compareAndExchangeReference(startNode, offset, expectedNext, head);
349
if (!res.equals(expectedNext)) {
350
throw new Error("Fail CmpX should always succeed on thread local objects, check your barrier implementation");
351
}
352
return expectedNext; // continue on old circle
353
}
354
355
private Node mergeImplCMPXFail(Node startNode, Node expectedNext, Node head) {
356
Object res = UNSAFE.compareAndExchangeReference(startNode, offset, head, head);
357
if (startNode.next() != expectedNext) {
358
throw new Error("Shouldn't have changed");
359
}
360
if (head == expectedNext) {
361
throw new Error("Test malfunction");
362
}
363
if (!res.equals(expectedNext)) {
364
throw new Error("This CmpX should have returned 'expectedNext' when it failed");
365
}
366
if (res.equals(head)) {
367
throw new Error("This CmpX shouldn't have returned head when it failed. count: "+ iterations);
368
}
369
370
return current;
371
}
372
373
// Create a new branch that will replace a part of the circle
374
public Node makeBranch(Node end_node, int count) {
375
Node head = end_node;
376
for (int i = 0; i < count; i++) {
377
head = new Node(head);
378
}
379
return head;
380
}
381
}
382
383
class Node {
384
Node next;
385
int number = 0;
386
387
public int number() {
388
return number;
389
}
390
391
public void setNumber(int v) {
392
number = v;
393
}
394
395
public Node() {
396
}
397
398
public Node(Node link) {
399
next = link;
400
}
401
402
public void setNext(Node next) {
403
this.next = next;
404
}
405
public Node next() {
406
return next;
407
}
408
}
409
410