1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import java.lang.reflect.Field;
18import java.util.concurrent.atomic.AtomicBoolean;
19
20import sun.misc.Unsafe;
21
22/**
23 * Checker test on the 1.8 unsafe operations. Note, this is by no means an
24 * exhaustive unit test for these CAS (compare-and-swap) and fence operations.
25 * Instead, this test ensures the methods are recognized as intrinsic and behave
26 * as expected.
27 */
28public class Main {
29
30  private static final Unsafe unsafe = getUnsafe();
31
32  private static Thread[] sThreads = new Thread[10];
33
34  //
35  // Fields accessed by setters and adders, and by memory fence tests.
36  //
37
38  public int i = 0;
39  public long l = 0;
40  public Object o = null;
41
42  public int x_value;
43  public int y_value;
44  public volatile boolean running;
45
46  //
47  // Setters.
48  //
49
50  /// CHECK-START: int Main.set32(java.lang.Object, long, int) intrinsics_recognition (after)
51  /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetInt
52  /// CHECK-DAG:                 Return [<<Result>>]
53  private static int set32(Object o, long offset, int newValue) {
54    return unsafe.getAndSetInt(o, offset, newValue);
55  }
56
57  /// CHECK-START: long Main.set64(java.lang.Object, long, long) intrinsics_recognition (after)
58  /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetLong
59  /// CHECK-DAG:                 Return [<<Result>>]
60  private static long set64(Object o, long offset, long newValue) {
61    return unsafe.getAndSetLong(o, offset, newValue);
62  }
63
64  /// CHECK-START: java.lang.Object Main.setObj(java.lang.Object, long, java.lang.Object) intrinsics_recognition (after)
65  /// CHECK-DAG: <<Result:l\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetObject
66  /// CHECK-DAG:                 Return [<<Result>>]
67  private static Object setObj(Object o, long offset, Object newValue) {
68    return unsafe.getAndSetObject(o, offset, newValue);
69  }
70
71  //
72  // Adders.
73  //
74
75  /// CHECK-START: int Main.add32(java.lang.Object, long, int) intrinsics_recognition (after)
76  /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddInt
77  /// CHECK-DAG:                 Return [<<Result>>]
78  private static int add32(Object o, long offset, int delta) {
79    return unsafe.getAndAddInt(o, offset, delta);
80  }
81
82  /// CHECK-START: long Main.add64(java.lang.Object, long, long) intrinsics_recognition (after)
83  /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddLong
84  /// CHECK-DAG:                 Return [<<Result>>]
85  private static long add64(Object o, long offset, long delta) {
86    return unsafe.getAndAddLong(o, offset, delta);
87  }
88
89  //
90  // Fences (native).
91  //
92
93  /// CHECK-START: void Main.load() intrinsics_recognition (after)
94  /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence
95  //
96  /// CHECK-START: void Main.load() instruction_simplifier (after)
97  /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeLoadFence
98  //
99  /// CHECK-START: void Main.load() instruction_simplifier (after)
100  /// CHECK-DAG: MemoryBarrier kind:LoadAny
101  private static void load() {
102    unsafe.loadFence();
103  }
104
105  /// CHECK-START: void Main.store() intrinsics_recognition (after)
106  /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence
107  //
108  /// CHECK-START: void Main.store() instruction_simplifier (after)
109  /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeStoreFence
110  //
111  /// CHECK-START: void Main.store() instruction_simplifier (after)
112  /// CHECK-DAG: MemoryBarrier kind:AnyStore
113  private static void store() {
114    unsafe.storeFence();
115  }
116
117  /// CHECK-START: void Main.full() intrinsics_recognition (after)
118  /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence
119  //
120  /// CHECK-START: void Main.full() instruction_simplifier (after)
121  /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeFullFence
122  //
123  /// CHECK-START: void Main.full() instruction_simplifier (after)
124  /// CHECK-DAG: MemoryBarrier kind:AnyAny
125  private static void full() {
126    unsafe.fullFence();
127  }
128
129  //
130  // Thread fork/join.
131  //
132
133  private static void fork(Runnable r) {
134    for (int i = 0; i < 10; i++) {
135      sThreads[i] = new Thread(r);
136    }
137    // Start the threads only after the full array has been written with new threads,
138    // because one test relies on the contents of this array to be consistent.
139    for (int i = 0; i < 10; i++) {
140      sThreads[i].start();
141    }
142  }
143
144  private static void join() {
145    try {
146      for (int i = 0; i < 10; i++) {
147        sThreads[i].join();
148      }
149    } catch (InterruptedException e) {
150      throw new Error("Failed join: " + e);
151    }
152  }
153
154  //
155  // Driver.
156  //
157
158  public static void main(String[] args) {
159    System.out.println("starting");
160
161    final Main m = new Main();
162
163    // Get the offsets.
164
165    final long intOffset, longOffset, objOffset;
166    try {
167      Field intField = Main.class.getDeclaredField("i");
168      Field longField = Main.class.getDeclaredField("l");
169      Field objField = Main.class.getDeclaredField("o");
170
171      intOffset = unsafe.objectFieldOffset(intField);
172      longOffset = unsafe.objectFieldOffset(longField);
173      objOffset = unsafe.objectFieldOffset(objField);
174
175    } catch (NoSuchFieldException e) {
176      throw new Error("No offset: " + e);
177    }
178
179    // Some sanity on setters and adders within same thread.
180
181    set32(m, intOffset, 3);
182    expectEqual32(3, m.i);
183
184    set64(m, longOffset, 7L);
185    expectEqual64(7L, m.l);
186
187    setObj(m, objOffset, m);
188    expectEqualObj(m, m.o);
189
190    add32(m, intOffset, 11);
191    expectEqual32(14, m.i);
192
193    add64(m, longOffset, 13L);
194    expectEqual64(20L, m.l);
195
196    // Some sanity on setters within different threads.
197
198    fork(new Runnable() {
199      public void run() {
200        for (int i = 0; i < 10; i++)
201          set32(m, intOffset, i);
202      }
203    });
204    join();
205    expectEqual32(9, m.i);  // one thread's last value wins
206
207    fork(new Runnable() {
208      public void run() {
209        for (int i = 0; i < 10; i++)
210          set64(m, longOffset, (long) (100 + i));
211      }
212    });
213    join();
214    expectEqual64(109L, m.l);  // one thread's last value wins
215
216    fork(new Runnable() {
217      public void run() {
218        for (int i = 0; i < 10; i++)
219          setObj(m, objOffset, sThreads[i]);
220      }
221    });
222    join();
223    expectEqualObj(sThreads[9], m.o);  // one thread's last value wins
224
225    // Some sanity on adders within different threads.
226
227    fork(new Runnable() {
228      public void run() {
229        for (int i = 0; i < 10; i++)
230          add32(m, intOffset, i + 1);
231      }
232    });
233    join();
234    expectEqual32(559, m.i);  // all values accounted for
235
236    fork(new Runnable() {
237      public void run() {
238        for (int i = 0; i < 10; i++)
239          add64(m, longOffset, (long) (i + 1));
240      }
241    });
242    join();
243    expectEqual64(659L, m.l);  // all values accounted for
244
245    // Some sanity on fences within same thread. Note that memory fences within one
246    // thread make little sense, but the sanity check ensures nothing bad happens.
247
248    m.i = -1;
249    m.l = -2L;
250    m.o = null;
251
252    load();
253    store();
254    full();
255
256    expectEqual32(-1, m.i);
257    expectEqual64(-2L, m.l);
258    expectEqualObj(null, m.o);
259
260    // Some sanity on full fence within different threads. We write the non-volatile m.l after
261    // the fork(), which means there is no happens-before relation in the Java memory model
262    // with respect to the read in the threads. This relation is enforced by the memory fences
263    // and the weak-set() -> get() guard. Note that the guard semantics used here are actually
264    // too strong and already enforce total memory visibility, but this test illustrates what
265    // should still happen if Java had a true relaxed memory guard.
266
267    final AtomicBoolean guard1 = new AtomicBoolean();
268    m.l = 0L;
269
270    fork(new Runnable() {
271      public void run() {
272        while (!guard1.get());  // busy-waiting
273        full();
274        expectEqual64(-123456789L, m.l);
275      }
276    });
277
278    m.l = -123456789L;
279    full();
280    while (!guard1.weakCompareAndSet(false, true));  // relaxed memory order
281    join();
282
283    // Some sanity on release/acquire fences within different threads. We write the non-volatile
284    // m.l after the fork(), which means there is no happens-before relation in the Java memory
285    // model with respect to the read in the threads. This relation is enforced by the memory fences
286    // and the weak-set() -> get() guard. Note that the guard semantics used here are actually
287    // too strong and already enforce total memory visibility, but this test illustrates what
288    // should still happen if Java had a true relaxed memory guard.
289
290    final AtomicBoolean guard2 = new AtomicBoolean();
291    m.l = 0L;
292
293    fork(new Runnable() {
294      public void run() {
295        while (!guard2.get());  // busy-waiting
296        load();
297        expectEqual64(-987654321L, m.l);
298      }
299    });
300
301    m.l = -987654321L;
302    store();
303    while (!guard2.weakCompareAndSet(false, true));  // relaxed memory order
304    join();
305
306    // Some sanity on release/acquire fences within different threads using a test suggested by
307    // Hans Boehm. Even this test remains with the realm of sanity only, since having the threads
308    // read the same value consistently would be a valid outcome.
309
310    m.x_value = -1;
311    m.y_value = -1;
312    m.running = true;
313
314    fork(new Runnable() {
315      public void run() {
316        while (m.running) {
317          for (int few_times = 0; few_times < 1000; few_times++) {
318            // Read y first, then load fence, then read x.
319            // They should appear in order, if seen at all.
320            int local_y = m.y_value;
321            load();
322            int local_x = m.x_value;
323            expectLessThanOrEqual32(local_y, local_x);
324          }
325        }
326      }
327    });
328
329    for (int many_times = 0; many_times < 100000; many_times++) {
330      m.x_value = many_times;
331      store();
332      m.y_value = many_times;
333    }
334    m.running = false;
335    join();
336
337    // All done!
338
339    System.out.println("passed");
340  }
341
342  // Use reflection to implement "Unsafe.getUnsafe()";
343  private static Unsafe getUnsafe() {
344    try {
345      Class<?> unsafeClass = Unsafe.class;
346      Field f = unsafeClass.getDeclaredField("theUnsafe");
347      f.setAccessible(true);
348      return (Unsafe) f.get(null);
349    } catch (Exception e) {
350      throw new Error("Cannot get Unsafe instance");
351    }
352  }
353
354  private static void expectEqual32(int expected, int result) {
355    if (expected != result) {
356      throw new Error("Expected: " + expected + ", found: " + result);
357    }
358  }
359
360  private static void expectLessThanOrEqual32(int val1, int val2) {
361    if (val1 > val2) {
362      throw new Error("Expected: " + val1 + " <= " + val2);
363    }
364  }
365
366  private static void expectEqual64(long expected, long result) {
367    if (expected != result) {
368      throw new Error("Expected: " + expected + ", found: " + result);
369    }
370  }
371
372  private static void expectEqualObj(Object expected, Object result) {
373    if (expected != result) {
374      throw new Error("Expected: " + expected + ", found: " + result);
375    }
376  }
377}
378