content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  import java.lang.reflect.Field;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  import sun.misc.Unsafe;
  
  /**
   * Checker test on the 1.8 unsafe operations. Note, this is by no means an
   * exhaustive unit test for these CAS (compare-and-swap) and fence operations.
   * Instead, this test ensures the methods are recognized as intrinsic and behave
   * as expected.
   */
  public class Main {
  
    private static final Unsafe unsafe = getUnsafe();
  
    private static Thread[] sThreads = new Thread[10];
  
    //
    // Fields accessed by setters and adders, and by memory fence tests.
    //
  
    public int i = 0;
    public long l = 0;
    public Object o = null;
  
    public int x_value;
    public int y_value;
    public volatile boolean running;
  
    //
    // Setters.
    //
  
    /// CHECK-START: int Main.set32(java.lang.Object, long, int) intrinsics_recognition (after)
    /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetInt
    /// CHECK-DAG:                 Return [<<Result>>]
    private static int set32(Object o, long offset, int newValue) {
      return unsafe.getAndSetInt(o, offset, newValue);
    }
  
    /// CHECK-START: long Main.set64(java.lang.Object, long, long) intrinsics_recognition (after)
    /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetLong
    /// CHECK-DAG:                 Return [<<Result>>]
    private static long set64(Object o, long offset, long newValue) {
      return unsafe.getAndSetLong(o, offset, newValue);
    }
  
    /// CHECK-START: java.lang.Object Main.setObj(java.lang.Object, long, java.lang.Object) intrinsics_recognition (after)
    /// CHECK-DAG: <<Result:l\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetObject
    /// CHECK-DAG:                 Return [<<Result>>]
    private static Object setObj(Object o, long offset, Object newValue) {
      return unsafe.getAndSetObject(o, offset, newValue);
    }
  
    //
    // Adders.
    //
  
    /// CHECK-START: int Main.add32(java.lang.Object, long, int) intrinsics_recognition (after)
    /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddInt
    /// CHECK-DAG:                 Return [<<Result>>]
    private static int add32(Object o, long offset, int delta) {
      return unsafe.getAndAddInt(o, offset, delta);
    }
  
    /// CHECK-START: long Main.add64(java.lang.Object, long, long) intrinsics_recognition (after)
    /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddLong
    /// CHECK-DAG:                 Return [<<Result>>]
    private static long add64(Object o, long offset, long delta) {
      return unsafe.getAndAddLong(o, offset, delta);
    }
  
    //
    // Fences (native).
    //
  
    /// CHECK-START: void Main.load() intrinsics_recognition (after)
    /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence
    //
    /// CHECK-START: void Main.load() instruction_simplifier (after)
    /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeLoadFence
    //
    /// CHECK-START: void Main.load() instruction_simplifier (after)
    /// CHECK-DAG: MemoryBarrier kind:LoadAny
    private static void load() {
      unsafe.loadFence();
    }
  
    /// CHECK-START: void Main.store() intrinsics_recognition (after)
    /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence
    //
    /// CHECK-START: void Main.store() instruction_simplifier (after)
    /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeStoreFence
    //
    /// CHECK-START: void Main.store() instruction_simplifier (after)
    /// CHECK-DAG: MemoryBarrier kind:AnyStore
    private static void store() {
      unsafe.storeFence();
    }
  
    /// CHECK-START: void Main.full() intrinsics_recognition (after)
    /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence
    //
    /// CHECK-START: void Main.full() instruction_simplifier (after)
    /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeFullFence
    //
    /// CHECK-START: void Main.full() instruction_simplifier (after)
    /// CHECK-DAG: MemoryBarrier kind:AnyAny
    private static void full() {
      unsafe.fullFence();
    }
  
    //
    // Thread fork/join.
    //
  
    private static void fork(Runnable r) {
      for (int i = 0; i < 10; i++) {
        sThreads[i] = new Thread(r);
      }
      // Start the threads only after the full array has been written with new threads,
      // because one test relies on the contents of this array to be consistent.
      for (int i = 0; i < 10; i++) {
        sThreads[i].start();
      }
    }
  
    private static void join() {
      try {
        for (int i = 0; i < 10; i++) {
          sThreads[i].join();
        }
      } catch (InterruptedException e) {
        throw new Error("Failed join: " + e);
      }
    }
  
    //
    // Driver.
    //
  
    public static void main(String[] args) {
      System.out.println("starting");
  
      final Main m = new Main();
  
      // Get the offsets.
  
      final long intOffset, longOffset, objOffset;
      try {
        Field intField = Main.class.getDeclaredField("i");
        Field longField = Main.class.getDeclaredField("l");
        Field objField = Main.class.getDeclaredField("o");
  
        intOffset = unsafe.objectFieldOffset(intField);
        longOffset = unsafe.objectFieldOffset(longField);
        objOffset = unsafe.objectFieldOffset(objField);
  
      } catch (NoSuchFieldException e) {
        throw new Error("No offset: " + e);
      }
  
      // Some sanity on setters and adders within same thread.
  
      set32(m, intOffset, 3);
      expectEqual32(3, m.i);
  
      set64(m, longOffset, 7L);
      expectEqual64(7L, m.l);
  
      setObj(m, objOffset, m);
      expectEqualObj(m, m.o);
  
      add32(m, intOffset, 11);
      expectEqual32(14, m.i);
  
      add64(m, longOffset, 13L);
      expectEqual64(20L, m.l);
  
      // Some sanity on setters within different threads.
  
      fork(new Runnable() {
        public void run() {
          for (int i = 0; i < 10; i++)
            set32(m, intOffset, i);
        }
      });
      join();
      expectEqual32(9, m.i);  // one thread's last value wins
  
      fork(new Runnable() {
        public void run() {
          for (int i = 0; i < 10; i++)
            set64(m, longOffset, (long) (100 + i));
        }
      });
      join();
      expectEqual64(109L, m.l);  // one thread's last value wins
  
      fork(new Runnable() {
        public void run() {
          for (int i = 0; i < 10; i++)
            setObj(m, objOffset, sThreads[i]);
        }
      });
      join();
      expectEqualObj(sThreads[9], m.o);  // one thread's last value wins
  
      // Some sanity on adders within different threads.
  
      fork(new Runnable() {
        public void run() {
          for (int i = 0; i < 10; i++)
            add32(m, intOffset, i + 1);
        }
      });
      join();
      expectEqual32(559, m.i);  // all values accounted for
  
      fork(new Runnable() {
        public void run() {
          for (int i = 0; i < 10; i++)
            add64(m, longOffset, (long) (i + 1));
        }
      });
      join();
      expectEqual64(659L, m.l);  // all values accounted for
  
      // Some sanity on fences within same thread. Note that memory fences within one
      // thread make little sense, but the sanity check ensures nothing bad happens.
  
      m.i = -1;
      m.l = -2L;
      m.o = null;
  
      load();
      store();
      full();
  
      expectEqual32(-1, m.i);
      expectEqual64(-2L, m.l);
      expectEqualObj(null, m.o);
  
      // Some sanity on full fence within different threads. We write the non-volatile m.l after
      // the fork(), which means there is no happens-before relation in the Java memory model
      // with respect to the read in the threads. This relation is enforced by the memory fences
      // and the weak-set() -> get() guard. Note that the guard semantics used here are actually
      // too strong and already enforce total memory visibility, but this test illustrates what
      // should still happen if Java had a true relaxed memory guard.
  
      final AtomicBoolean guard1 = new AtomicBoolean();
      m.l = 0L;
  
      fork(new Runnable() {
        public void run() {
          while (!guard1.get());  // busy-waiting
          full();
          expectEqual64(-123456789L, m.l);
        }
      });
  
      m.l = -123456789L;
      full();
      while (!guard1.weakCompareAndSet(false, true));  // relaxed memory order
      join();
  
      // Some sanity on release/acquire fences within different threads. We write the non-volatile
      // m.l after the fork(), which means there is no happens-before relation in the Java memory
      // model with respect to the read in the threads. This relation is enforced by the memory fences
      // and the weak-set() -> get() guard. Note that the guard semantics used here are actually
      // too strong and already enforce total memory visibility, but this test illustrates what
      // should still happen if Java had a true relaxed memory guard.
  
      final AtomicBoolean guard2 = new AtomicBoolean();
      m.l = 0L;
  
      fork(new Runnable() {
        public void run() {
          while (!guard2.get());  // busy-waiting
          load();
          expectEqual64(-987654321L, m.l);
        }
      });
  
      m.l = -987654321L;
      store();
      while (!guard2.weakCompareAndSet(false, true));  // relaxed memory order
      join();
  
      // Some sanity on release/acquire fences within different threads using a test suggested by
      // Hans Boehm. Even this test remains with the realm of sanity only, since having the threads
      // read the same value consistently would be a valid outcome.
  
      m.x_value = -1;
      m.y_value = -1;
      m.running = true;
  
      fork(new Runnable() {
        public void run() {
          while (m.running) {
            for (int few_times = 0; few_times < 1000; few_times++) {
              // Read y first, then load fence, then read x.
              // They should appear in order, if seen at all.
              int local_y = m.y_value;
              load();
              int local_x = m.x_value;
              expectLessThanOrEqual32(local_y, local_x);
            }
          }
        }
      });
  
      for (int many_times = 0; many_times < 100000; many_times++) {
        m.x_value = many_times;
        store();
        m.y_value = many_times;
      }
      m.running = false;
      join();
  
      // All done!
  
      System.out.println("passed");
    }
  
    // Use reflection to implement "Unsafe.getUnsafe()";
    private static Unsafe getUnsafe() {
      try {
        Class<?> unsafeClass = Unsafe.class;
        Field f = unsafeClass.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        return (Unsafe) f.get(null);
      } catch (Exception e) {
        throw new Error("Cannot get Unsafe instance");
      }
    }
  
    private static void expectEqual32(int expected, int result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    private static void expectLessThanOrEqual32(int val1, int val2) {
      if (val1 > val2) {
        throw new Error("Expected: " + val1 + " <= " + val2);
      }
    }
  
    private static void expectEqual64(long expected, long result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    private static void expectEqualObj(Object expected, Object result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  }
  