Home | History | Annotate | Download | only in src
      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>>]
     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