• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 import java.lang.reflect.Field;
18 import java.util.concurrent.atomic.AtomicBoolean;
19 
20 import 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  */
28 public 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>>]
set32(Object o, long offset, int newValue)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>>]
set64(Object o, long offset, long newValue)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>>]
setObj(Object o, long offset, Object newValue)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>>]
add32(Object o, long offset, int delta)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>>]
add64(Object o, long offset, long delta)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
load()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
store()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
full()125   private static void full() {
126     unsafe.fullFence();
127   }
128 
129   //
130   // Thread fork/join.
131   //
132 
fork(Runnable r)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 
join()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 
main(String[] args)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()";
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 
expectEqual32(int expected, int result)354   private static void expectEqual32(int expected, int result) {
355     if (expected != result) {
356       throw new Error("Expected: " + expected + ", found: " + result);
357     }
358   }
359 
expectLessThanOrEqual32(int val1, int val2)360   private static void expectLessThanOrEqual32(int val1, int val2) {
361     if (val1 > val2) {
362       throw new Error("Expected: " + val1 + " <= " + val2);
363     }
364   }
365 
expectEqual64(long expected, long result)366   private static void expectEqual64(long expected, long result) {
367     if (expected != result) {
368       throw new Error("Expected: " + expected + ", found: " + result);
369     }
370   }
371 
expectEqualObj(Object expected, Object result)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