Aart Bik | 0e54c01 | 2016-03-04 12:08:31 -0800 | [diff] [blame] | 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 | |
| 19 | import sun.misc.Unsafe; |
| 20 | |
| 21 | /** |
| 22 | * Checker test on the 1.8 unsafe operations. Note, this is by no means an |
| 23 | * exhaustive unit test for these CAS (compare-and-swap) and fence operations. |
| 24 | * Instead, this test ensures the methods are recognized as intrinsic and behave |
| 25 | * as expected. |
| 26 | */ |
| 27 | public class Main { |
| 28 | |
| 29 | private static final Unsafe unsafe = getUnsafe(); |
| 30 | |
| 31 | private static Thread[] sThreads = new Thread[10]; |
| 32 | |
| 33 | // |
| 34 | // Fields accessed by setters and adders. |
| 35 | // |
| 36 | |
| 37 | public int i = 0; |
| 38 | public long l = 0; |
| 39 | public Object o = null; |
| 40 | |
| 41 | // |
| 42 | // Setters. |
| 43 | // |
| 44 | |
| 45 | /// CHECK-START: int Main.set32(java.lang.Object, long, int) intrinsics_recognition (after) |
| 46 | /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetInt |
| 47 | /// CHECK-DAG: Return [<<Result>>] |
| 48 | private static int set32(Object o, long offset, int newValue) { |
| 49 | return unsafe.getAndSetInt(o, offset, newValue); |
| 50 | } |
| 51 | |
| 52 | /// CHECK-START: long Main.set64(java.lang.Object, long, long) intrinsics_recognition (after) |
| 53 | /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetLong |
| 54 | /// CHECK-DAG: Return [<<Result>>] |
| 55 | private static long set64(Object o, long offset, long newValue) { |
| 56 | return unsafe.getAndSetLong(o, offset, newValue); |
| 57 | } |
| 58 | |
| 59 | /// CHECK-START: java.lang.Object Main.setObj(java.lang.Object, long, java.lang.Object) intrinsics_recognition (after) |
| 60 | /// CHECK-DAG: <<Result:l\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetObject |
| 61 | /// CHECK-DAG: Return [<<Result>>] |
| 62 | private static Object setObj(Object o, long offset, Object newValue) { |
| 63 | return unsafe.getAndSetObject(o, offset, newValue); |
| 64 | } |
| 65 | |
| 66 | // |
| 67 | // Adders. |
| 68 | // |
| 69 | |
| 70 | /// CHECK-START: int Main.add32(java.lang.Object, long, int) intrinsics_recognition (after) |
| 71 | /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddInt |
| 72 | /// CHECK-DAG: Return [<<Result>>] |
| 73 | private static int add32(Object o, long offset, int delta) { |
| 74 | return unsafe.getAndAddInt(o, offset, delta); |
| 75 | } |
| 76 | |
| 77 | /// CHECK-START: long Main.add64(java.lang.Object, long, long) intrinsics_recognition (after) |
| 78 | /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddLong |
| 79 | /// CHECK-DAG: Return [<<Result>>] |
| 80 | private static long add64(Object o, long offset, long delta) { |
| 81 | return unsafe.getAndAddLong(o, offset, delta); |
| 82 | } |
| 83 | |
| 84 | // |
| 85 | // Fences (native). |
| 86 | // |
| 87 | |
| 88 | /// CHECK-START: void Main.load() intrinsics_recognition (after) |
| 89 | /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence |
| 90 | private static void load() { |
| 91 | unsafe.loadFence(); |
| 92 | } |
| 93 | |
| 94 | /// CHECK-START: void Main.store() intrinsics_recognition (after) |
| 95 | /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence |
| 96 | private static void store() { |
| 97 | unsafe.storeFence(); |
| 98 | } |
| 99 | |
| 100 | /// CHECK-START: void Main.full() intrinsics_recognition (after) |
| 101 | /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence |
| 102 | private static void full() { |
| 103 | unsafe.fullFence(); |
| 104 | } |
| 105 | |
| 106 | // |
| 107 | // Thread fork/join. |
| 108 | // |
| 109 | |
| 110 | private static void fork(Runnable r) { |
| 111 | for (int i = 0; i < 10; i++) { |
| 112 | sThreads[i] = new Thread(r); |
| 113 | sThreads[i].start(); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | private static void join() { |
| 118 | try { |
| 119 | for (int i = 0; i < 10; i++) { |
| 120 | sThreads[i].join(); |
| 121 | } |
| 122 | } catch (InterruptedException e) { |
| 123 | throw new Error("Failed join: " + e); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | // |
| 128 | // Driver. |
| 129 | // |
| 130 | |
| 131 | public static void main(String[] args) { |
| 132 | System.out.println("starting"); |
| 133 | |
| 134 | final Main m = new Main(); |
| 135 | |
| 136 | // Get the offsets. |
| 137 | |
| 138 | final long intOffset, longOffset, objOffset; |
| 139 | try { |
| 140 | Field intField = Main.class.getDeclaredField("i"); |
| 141 | Field longField = Main.class.getDeclaredField("l"); |
| 142 | Field objField = Main.class.getDeclaredField("o"); |
| 143 | |
| 144 | intOffset = unsafe.objectFieldOffset(intField); |
| 145 | longOffset = unsafe.objectFieldOffset(longField); |
| 146 | objOffset = unsafe.objectFieldOffset(objField); |
| 147 | |
| 148 | } catch (NoSuchFieldException e) { |
| 149 | throw new Error("No offset: " + e); |
| 150 | } |
| 151 | |
| 152 | // Some sanity within same thread. |
| 153 | |
| 154 | set32(m, intOffset, 3); |
| 155 | expectEquals32(3, m.i); |
| 156 | |
| 157 | set64(m, longOffset, 7L); |
| 158 | expectEquals64(7L, m.l); |
| 159 | |
| 160 | setObj(m, objOffset, m); |
| 161 | expectEqualsObj(m, m.o); |
| 162 | |
| 163 | add32(m, intOffset, 11); |
| 164 | expectEquals32(14, m.i); |
| 165 | |
| 166 | add64(m, longOffset, 13L); |
| 167 | expectEquals64(20L, m.l); |
| 168 | |
| 169 | // Some sanity on setters within different threads. |
| 170 | |
| 171 | fork(new Runnable() { |
| 172 | public void run() { |
| 173 | for (int i = 0; i < 10; i++) |
| 174 | set32(m, intOffset, i); |
| 175 | } |
| 176 | }); |
| 177 | join(); |
| 178 | expectEquals32(9, m.i); // one thread's last value wins |
| 179 | |
| 180 | fork(new Runnable() { |
| 181 | public void run() { |
| 182 | for (int i = 0; i < 10; i++) |
| 183 | set64(m, longOffset, (long) (100 + i)); |
| 184 | } |
| 185 | }); |
| 186 | join(); |
| 187 | expectEquals64(109L, m.l); // one thread's last value wins |
| 188 | |
| 189 | fork(new Runnable() { |
| 190 | public void run() { |
| 191 | for (int i = 0; i < 10; i++) |
| 192 | setObj(m, objOffset, sThreads[i]); |
| 193 | } |
| 194 | }); |
| 195 | join(); |
| 196 | expectEqualsObj(sThreads[9], m.o); // one thread's last value wins |
| 197 | |
| 198 | // Some sanity on adders within different threads. |
| 199 | |
| 200 | fork(new Runnable() { |
| 201 | public void run() { |
| 202 | for (int i = 0; i < 10; i++) |
| 203 | add32(m, intOffset, i + 1); |
| 204 | } |
| 205 | }); |
| 206 | join(); |
| 207 | expectEquals32(559, m.i); // all values accounted for |
| 208 | |
| 209 | fork(new Runnable() { |
| 210 | public void run() { |
| 211 | for (int i = 0; i < 10; i++) |
| 212 | add64(m, longOffset, (long) (i + 1)); |
| 213 | } |
| 214 | }); |
| 215 | join(); |
| 216 | expectEquals64(659L, m.l); // all values accounted for |
| 217 | |
| 218 | // TODO: the fences |
| 219 | |
| 220 | System.out.println("passed"); |
| 221 | } |
| 222 | |
| 223 | // Use reflection to implement "Unsafe.getUnsafe()"; |
| 224 | private static Unsafe getUnsafe() { |
| 225 | try { |
| 226 | Class<?> unsafeClass = Unsafe.class; |
| 227 | Field f = unsafeClass.getDeclaredField("theUnsafe"); |
| 228 | f.setAccessible(true); |
| 229 | return (Unsafe) f.get(null); |
| 230 | } catch (Exception e) { |
| 231 | throw new Error("Cannot get Unsafe instance"); |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | private static void expectEquals32(int expected, int result) { |
| 236 | if (expected != result) { |
| 237 | throw new Error("Expected: " + expected + ", found: " + result); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | private static void expectEquals64(long expected, long result) { |
| 242 | if (expected != result) { |
| 243 | throw new Error("Expected: " + expected + ", found: " + result); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | private static void expectEqualsObj(Object expected, Object result) { |
| 248 | if (expected != result) { |
| 249 | throw new Error("Expected: " + expected + ", found: " + result); |
| 250 | } |
| 251 | } |
| 252 | } |