1*795d594fSAndroid Build Coastguard Worker /* 2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2023 The Android Open Source Project 3*795d594fSAndroid Build Coastguard Worker * 4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*795d594fSAndroid Build Coastguard Worker * 8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*795d594fSAndroid Build Coastguard Worker * 10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*795d594fSAndroid Build Coastguard Worker * limitations under the License. 15*795d594fSAndroid Build Coastguard Worker */ 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker import java.lang.invoke.MethodHandles; 18*795d594fSAndroid Build Coastguard Worker import java.lang.invoke.VarHandle; 19*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.Field; 20*795d594fSAndroid Build Coastguard Worker import java.util.concurrent.atomic.AtomicReference; 21*795d594fSAndroid Build Coastguard Worker import sun.misc.Unsafe; 22*795d594fSAndroid Build Coastguard Worker 23*795d594fSAndroid Build Coastguard Worker class Main { main(String args[])24*795d594fSAndroid Build Coastguard Worker public static void main(String args[]) throws Exception { 25*795d594fSAndroid Build Coastguard Worker // Stress-test read-modify-write operations in adjacent memory locations. 26*795d594fSAndroid Build Coastguard Worker // This is intended to uncover bugs triggered by spurious CAS failures on 27*795d594fSAndroid Build Coastguard Worker // architectures where such spurious failures can happen. Bug: 218453177 28*795d594fSAndroid Build Coastguard Worker $noinline$testVarHandleBytes(); 29*795d594fSAndroid Build Coastguard Worker $noinline$testVarHandleInts(); 30*795d594fSAndroid Build Coastguard Worker $noinline$testVarHandleLongs(); 31*795d594fSAndroid Build Coastguard Worker $noinline$testVarHandleReferences(); 32*795d594fSAndroid Build Coastguard Worker $noinline$testUnsafeInts(); 33*795d594fSAndroid Build Coastguard Worker $noinline$testUnsafeLongs(); 34*795d594fSAndroid Build Coastguard Worker $noinline$testUnsafeReferences(); 35*795d594fSAndroid Build Coastguard Worker 36*795d594fSAndroid Build Coastguard Worker // Stress-test read-modify-write operations on the same memory locations. 37*795d594fSAndroid Build Coastguard Worker // This is intended to uncover bugs with false-positive comparison in CAS. 38*795d594fSAndroid Build Coastguard Worker $noinline$testAtomicReference(); 39*795d594fSAndroid Build Coastguard Worker } 40*795d594fSAndroid Build Coastguard Worker $noinline$testVarHandleBytes()41*795d594fSAndroid Build Coastguard Worker public static void $noinline$testVarHandleBytes() throws Exception { 42*795d594fSAndroid Build Coastguard Worker // Prepare `VarHandle` objects. 43*795d594fSAndroid Build Coastguard Worker VarHandle[] vhs = new VarHandle[] { 44*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourBytes.class, "b1", byte.class), 45*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourBytes.class, "b2", byte.class), 46*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourBytes.class, "b3", byte.class), 47*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourBytes.class, "b4", byte.class) 48*795d594fSAndroid Build Coastguard Worker }; 49*795d594fSAndroid Build Coastguard Worker // Prepare threads. 50*795d594fSAndroid Build Coastguard Worker final FourBytes fourBytes = new FourBytes(); 51*795d594fSAndroid Build Coastguard Worker final StopFlag stopFlag = new StopFlag(); 52*795d594fSAndroid Build Coastguard Worker Thread[] threads = new Thread[4]; 53*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 54*795d594fSAndroid Build Coastguard Worker final VarHandle vh = vhs[i]; 55*795d594fSAndroid Build Coastguard Worker threads[i] = new Thread() { 56*795d594fSAndroid Build Coastguard Worker public void run() { 57*795d594fSAndroid Build Coastguard Worker byte value = 0; 58*795d594fSAndroid Build Coastguard Worker while (!stopFlag.stop) { 59*795d594fSAndroid Build Coastguard Worker byte nextValue = (byte) (value + 1); 60*795d594fSAndroid Build Coastguard Worker boolean success = vh.compareAndSet(fourBytes, value, nextValue); 61*795d594fSAndroid Build Coastguard Worker assertTrue(success); 62*795d594fSAndroid Build Coastguard Worker value = nextValue; 63*795d594fSAndroid Build Coastguard Worker } 64*795d594fSAndroid Build Coastguard Worker } 65*795d594fSAndroid Build Coastguard Worker }; 66*795d594fSAndroid Build Coastguard Worker } 67*795d594fSAndroid Build Coastguard Worker // Start threads. 68*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 69*795d594fSAndroid Build Coastguard Worker threads[i].start(); 70*795d594fSAndroid Build Coastguard Worker } 71*795d594fSAndroid Build Coastguard Worker // Let the threads run for 5s. 72*795d594fSAndroid Build Coastguard Worker Thread.sleep(5000); 73*795d594fSAndroid Build Coastguard Worker // Stop threads. 74*795d594fSAndroid Build Coastguard Worker stopFlag.stop = true; 75*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 76*795d594fSAndroid Build Coastguard Worker threads[i].join(); 77*795d594fSAndroid Build Coastguard Worker } 78*795d594fSAndroid Build Coastguard Worker } 79*795d594fSAndroid Build Coastguard Worker $noinline$testVarHandleInts()80*795d594fSAndroid Build Coastguard Worker public static void $noinline$testVarHandleInts() throws Exception { 81*795d594fSAndroid Build Coastguard Worker // Prepare `VarHandle` objects. 82*795d594fSAndroid Build Coastguard Worker VarHandle[] vhs = new VarHandle[] { 83*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourInts.class, "i1", int.class), 84*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourInts.class, "i2", int.class), 85*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourInts.class, "i3", int.class), 86*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourInts.class, "i4", int.class) 87*795d594fSAndroid Build Coastguard Worker }; 88*795d594fSAndroid Build Coastguard Worker // Prepare threads. 89*795d594fSAndroid Build Coastguard Worker final FourInts fourInts = new FourInts(); 90*795d594fSAndroid Build Coastguard Worker final StopFlag stopFlag = new StopFlag(); 91*795d594fSAndroid Build Coastguard Worker Thread[] threads = new Thread[4]; 92*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 93*795d594fSAndroid Build Coastguard Worker final VarHandle vh = vhs[i]; 94*795d594fSAndroid Build Coastguard Worker threads[i] = new Thread() { 95*795d594fSAndroid Build Coastguard Worker public void run() { 96*795d594fSAndroid Build Coastguard Worker int value = 0; 97*795d594fSAndroid Build Coastguard Worker while (!stopFlag.stop) { 98*795d594fSAndroid Build Coastguard Worker int nextValue = value + 1; 99*795d594fSAndroid Build Coastguard Worker boolean success = vh.compareAndSet(fourInts, value, nextValue); 100*795d594fSAndroid Build Coastguard Worker assertTrue(success); 101*795d594fSAndroid Build Coastguard Worker value = nextValue; 102*795d594fSAndroid Build Coastguard Worker } 103*795d594fSAndroid Build Coastguard Worker } 104*795d594fSAndroid Build Coastguard Worker }; 105*795d594fSAndroid Build Coastguard Worker } 106*795d594fSAndroid Build Coastguard Worker // Start threads. 107*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 108*795d594fSAndroid Build Coastguard Worker threads[i].start(); 109*795d594fSAndroid Build Coastguard Worker } 110*795d594fSAndroid Build Coastguard Worker // Let the threads run for 5s. 111*795d594fSAndroid Build Coastguard Worker Thread.sleep(5000); 112*795d594fSAndroid Build Coastguard Worker // Stop threads. 113*795d594fSAndroid Build Coastguard Worker stopFlag.stop = true; 114*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 115*795d594fSAndroid Build Coastguard Worker threads[i].join(); 116*795d594fSAndroid Build Coastguard Worker } 117*795d594fSAndroid Build Coastguard Worker } 118*795d594fSAndroid Build Coastguard Worker $noinline$testVarHandleLongs()119*795d594fSAndroid Build Coastguard Worker public static void $noinline$testVarHandleLongs() throws Exception { 120*795d594fSAndroid Build Coastguard Worker // Prepare `VarHandle` objects. 121*795d594fSAndroid Build Coastguard Worker VarHandle[] vhs = new VarHandle[] { 122*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourLongs.class, "l1", long.class), 123*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourLongs.class, "l2", long.class), 124*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourLongs.class, "l3", long.class), 125*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourLongs.class, "l4", long.class) 126*795d594fSAndroid Build Coastguard Worker }; 127*795d594fSAndroid Build Coastguard Worker // Prepare threads. 128*795d594fSAndroid Build Coastguard Worker final FourLongs fourLongs = new FourLongs(); 129*795d594fSAndroid Build Coastguard Worker final StopFlag stopFlag = new StopFlag(); 130*795d594fSAndroid Build Coastguard Worker Thread[] threads = new Thread[4]; 131*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 132*795d594fSAndroid Build Coastguard Worker final VarHandle vh = vhs[i]; 133*795d594fSAndroid Build Coastguard Worker threads[i] = new Thread() { 134*795d594fSAndroid Build Coastguard Worker public void run() { 135*795d594fSAndroid Build Coastguard Worker long value = 0; 136*795d594fSAndroid Build Coastguard Worker while (!stopFlag.stop) { 137*795d594fSAndroid Build Coastguard Worker long nextValue = value + 1L; 138*795d594fSAndroid Build Coastguard Worker boolean success = vh.compareAndSet(fourLongs, value, nextValue); 139*795d594fSAndroid Build Coastguard Worker assertTrue(success); 140*795d594fSAndroid Build Coastguard Worker value = nextValue; 141*795d594fSAndroid Build Coastguard Worker } 142*795d594fSAndroid Build Coastguard Worker } 143*795d594fSAndroid Build Coastguard Worker }; 144*795d594fSAndroid Build Coastguard Worker } 145*795d594fSAndroid Build Coastguard Worker // Start threads. 146*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 147*795d594fSAndroid Build Coastguard Worker threads[i].start(); 148*795d594fSAndroid Build Coastguard Worker } 149*795d594fSAndroid Build Coastguard Worker // Let the threads run for 5s. 150*795d594fSAndroid Build Coastguard Worker Thread.sleep(5000); 151*795d594fSAndroid Build Coastguard Worker // Stop threads. 152*795d594fSAndroid Build Coastguard Worker stopFlag.stop = true; 153*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 154*795d594fSAndroid Build Coastguard Worker threads[i].join(); 155*795d594fSAndroid Build Coastguard Worker } 156*795d594fSAndroid Build Coastguard Worker } 157*795d594fSAndroid Build Coastguard Worker $noinline$testVarHandleReferences()158*795d594fSAndroid Build Coastguard Worker public static void $noinline$testVarHandleReferences() throws Exception { 159*795d594fSAndroid Build Coastguard Worker // Prepare `VarHandle` objects. 160*795d594fSAndroid Build Coastguard Worker VarHandle[] vhs = new VarHandle[] { 161*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourReferences.class, "r1", Object.class), 162*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourReferences.class, "r2", Object.class), 163*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourReferences.class, "r3", Object.class), 164*795d594fSAndroid Build Coastguard Worker MethodHandles.lookup().findVarHandle(FourReferences.class, "r4", Object.class) 165*795d594fSAndroid Build Coastguard Worker }; 166*795d594fSAndroid Build Coastguard Worker // Prepare threads. 167*795d594fSAndroid Build Coastguard Worker final FourReferences fourReferences = new FourReferences(); 168*795d594fSAndroid Build Coastguard Worker Object[] values = new Object[] { 169*795d594fSAndroid Build Coastguard Worker null, 170*795d594fSAndroid Build Coastguard Worker new Object(), 171*795d594fSAndroid Build Coastguard Worker new Object(), 172*795d594fSAndroid Build Coastguard Worker new Object() 173*795d594fSAndroid Build Coastguard Worker }; 174*795d594fSAndroid Build Coastguard Worker final StopFlag stopFlag = new StopFlag(); 175*795d594fSAndroid Build Coastguard Worker Thread[] threads = new Thread[4]; 176*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 177*795d594fSAndroid Build Coastguard Worker final VarHandle vh = vhs[i]; 178*795d594fSAndroid Build Coastguard Worker threads[i] = new Thread() { 179*795d594fSAndroid Build Coastguard Worker public void run() { 180*795d594fSAndroid Build Coastguard Worker int index = 0; 181*795d594fSAndroid Build Coastguard Worker while (!stopFlag.stop) { 182*795d594fSAndroid Build Coastguard Worker Object value = values[index]; 183*795d594fSAndroid Build Coastguard Worker index = (index + 1) & 3; 184*795d594fSAndroid Build Coastguard Worker Object nextValue = values[index]; 185*795d594fSAndroid Build Coastguard Worker boolean success = vh.compareAndSet(fourReferences, value, nextValue); 186*795d594fSAndroid Build Coastguard Worker assertTrue(success); 187*795d594fSAndroid Build Coastguard Worker } 188*795d594fSAndroid Build Coastguard Worker } 189*795d594fSAndroid Build Coastguard Worker }; 190*795d594fSAndroid Build Coastguard Worker } 191*795d594fSAndroid Build Coastguard Worker // Start threads. 192*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 193*795d594fSAndroid Build Coastguard Worker threads[i].start(); 194*795d594fSAndroid Build Coastguard Worker } 195*795d594fSAndroid Build Coastguard Worker // Allocate memory to trigger some GCs 196*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 640 * 1024; ++i) { 197*795d594fSAndroid Build Coastguard Worker $noinline$allocateAtLeast1KiB(); 198*795d594fSAndroid Build Coastguard Worker } 199*795d594fSAndroid Build Coastguard Worker // Stop threads. 200*795d594fSAndroid Build Coastguard Worker stopFlag.stop = true; 201*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 202*795d594fSAndroid Build Coastguard Worker threads[i].join(); 203*795d594fSAndroid Build Coastguard Worker } 204*795d594fSAndroid Build Coastguard Worker } 205*795d594fSAndroid Build Coastguard Worker $noinline$testUnsafeInts()206*795d594fSAndroid Build Coastguard Worker public static void $noinline$testUnsafeInts() throws Exception { 207*795d594fSAndroid Build Coastguard Worker // Prepare Unsafe offsets. 208*795d594fSAndroid Build Coastguard Worker final Unsafe unsafe = getUnsafe(); 209*795d594fSAndroid Build Coastguard Worker long[] offsets = new long[] { 210*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourInts.class.getField("i1")), 211*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourInts.class.getField("i2")), 212*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourInts.class.getField("i3")), 213*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourInts.class.getField("i4")) 214*795d594fSAndroid Build Coastguard Worker }; 215*795d594fSAndroid Build Coastguard Worker // Prepare threads. 216*795d594fSAndroid Build Coastguard Worker final FourInts fourInts = new FourInts(); 217*795d594fSAndroid Build Coastguard Worker final StopFlag stopFlag = new StopFlag(); 218*795d594fSAndroid Build Coastguard Worker Thread[] threads = new Thread[4]; 219*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 220*795d594fSAndroid Build Coastguard Worker final long offset = offsets[i]; 221*795d594fSAndroid Build Coastguard Worker threads[i] = new Thread() { 222*795d594fSAndroid Build Coastguard Worker public void run() { 223*795d594fSAndroid Build Coastguard Worker int value = 0; 224*795d594fSAndroid Build Coastguard Worker while (!stopFlag.stop) { 225*795d594fSAndroid Build Coastguard Worker int nextValue = value + 1; 226*795d594fSAndroid Build Coastguard Worker boolean success = unsafe.compareAndSwapInt( 227*795d594fSAndroid Build Coastguard Worker fourInts, offset, value, nextValue); 228*795d594fSAndroid Build Coastguard Worker assertTrue(success); 229*795d594fSAndroid Build Coastguard Worker value = nextValue; 230*795d594fSAndroid Build Coastguard Worker } 231*795d594fSAndroid Build Coastguard Worker } 232*795d594fSAndroid Build Coastguard Worker }; 233*795d594fSAndroid Build Coastguard Worker } 234*795d594fSAndroid Build Coastguard Worker // Start threads. 235*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 236*795d594fSAndroid Build Coastguard Worker threads[i].start(); 237*795d594fSAndroid Build Coastguard Worker } 238*795d594fSAndroid Build Coastguard Worker // Let the threads run for 5s. 239*795d594fSAndroid Build Coastguard Worker Thread.sleep(5000); 240*795d594fSAndroid Build Coastguard Worker // Stop threads. 241*795d594fSAndroid Build Coastguard Worker stopFlag.stop = true; 242*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 243*795d594fSAndroid Build Coastguard Worker threads[i].join(); 244*795d594fSAndroid Build Coastguard Worker } 245*795d594fSAndroid Build Coastguard Worker } 246*795d594fSAndroid Build Coastguard Worker $noinline$testUnsafeLongs()247*795d594fSAndroid Build Coastguard Worker public static void $noinline$testUnsafeLongs() throws Exception { 248*795d594fSAndroid Build Coastguard Worker // Prepare Unsafe offsets. 249*795d594fSAndroid Build Coastguard Worker final Unsafe unsafe = getUnsafe(); 250*795d594fSAndroid Build Coastguard Worker long[] offsets = new long[] { 251*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourLongs.class.getField("l1")), 252*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourLongs.class.getField("l2")), 253*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourLongs.class.getField("l3")), 254*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourLongs.class.getField("l4")) 255*795d594fSAndroid Build Coastguard Worker }; 256*795d594fSAndroid Build Coastguard Worker // Prepare threads. 257*795d594fSAndroid Build Coastguard Worker final FourLongs fourLongs = new FourLongs(); 258*795d594fSAndroid Build Coastguard Worker final StopFlag stopFlag = new StopFlag(); 259*795d594fSAndroid Build Coastguard Worker Thread[] threads = new Thread[4]; 260*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 261*795d594fSAndroid Build Coastguard Worker final long offset = offsets[i]; 262*795d594fSAndroid Build Coastguard Worker threads[i] = new Thread() { 263*795d594fSAndroid Build Coastguard Worker public void run() { 264*795d594fSAndroid Build Coastguard Worker long value = 0; 265*795d594fSAndroid Build Coastguard Worker while (!stopFlag.stop) { 266*795d594fSAndroid Build Coastguard Worker long nextValue = value + 1L; 267*795d594fSAndroid Build Coastguard Worker boolean success = unsafe.compareAndSwapLong( 268*795d594fSAndroid Build Coastguard Worker fourLongs, offset, value, nextValue); 269*795d594fSAndroid Build Coastguard Worker assertTrue(success); 270*795d594fSAndroid Build Coastguard Worker value = nextValue; 271*795d594fSAndroid Build Coastguard Worker } 272*795d594fSAndroid Build Coastguard Worker } 273*795d594fSAndroid Build Coastguard Worker }; 274*795d594fSAndroid Build Coastguard Worker } 275*795d594fSAndroid Build Coastguard Worker // Start threads. 276*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 277*795d594fSAndroid Build Coastguard Worker threads[i].start(); 278*795d594fSAndroid Build Coastguard Worker } 279*795d594fSAndroid Build Coastguard Worker // Let the threads run for 5s. 280*795d594fSAndroid Build Coastguard Worker Thread.sleep(5000); 281*795d594fSAndroid Build Coastguard Worker // Stop threads. 282*795d594fSAndroid Build Coastguard Worker stopFlag.stop = true; 283*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 284*795d594fSAndroid Build Coastguard Worker threads[i].join(); 285*795d594fSAndroid Build Coastguard Worker } 286*795d594fSAndroid Build Coastguard Worker } 287*795d594fSAndroid Build Coastguard Worker $noinline$testUnsafeReferences()288*795d594fSAndroid Build Coastguard Worker public static void $noinline$testUnsafeReferences() throws Exception { 289*795d594fSAndroid Build Coastguard Worker // Prepare Unsafe offsets. 290*795d594fSAndroid Build Coastguard Worker // D8 rewrites the bytecode with a workaround for CAS bug. To test the raw 291*795d594fSAndroid Build Coastguard Worker // `Unsafe.compareAndSwapObject()` call, we implement the call in smali 292*795d594fSAndroid Build Coastguard Worker // and wrap it in an indirect call. 293*795d594fSAndroid Build Coastguard Worker final UnsafeDispatch unsafeDispatch = 294*795d594fSAndroid Build Coastguard Worker (UnsafeDispatch) Class.forName("UnsafeWrapper").newInstance(); 295*795d594fSAndroid Build Coastguard Worker final Unsafe unsafe = getUnsafe(); 296*795d594fSAndroid Build Coastguard Worker long[] offsets = new long[] { 297*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourReferences.class.getField("r1")), 298*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourReferences.class.getField("r2")), 299*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourReferences.class.getField("r3")), 300*795d594fSAndroid Build Coastguard Worker unsafe.objectFieldOffset(FourReferences.class.getField("r4")) 301*795d594fSAndroid Build Coastguard Worker }; 302*795d594fSAndroid Build Coastguard Worker // Prepare threads. 303*795d594fSAndroid Build Coastguard Worker final FourReferences fourReferences = new FourReferences(); 304*795d594fSAndroid Build Coastguard Worker Object[] values = new Object[] { 305*795d594fSAndroid Build Coastguard Worker null, 306*795d594fSAndroid Build Coastguard Worker new Object(), 307*795d594fSAndroid Build Coastguard Worker new Object(), 308*795d594fSAndroid Build Coastguard Worker new Object() 309*795d594fSAndroid Build Coastguard Worker }; 310*795d594fSAndroid Build Coastguard Worker final StopFlag stopFlag = new StopFlag(); 311*795d594fSAndroid Build Coastguard Worker Thread[] threads = new Thread[4]; 312*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 313*795d594fSAndroid Build Coastguard Worker final long offset = offsets[i]; 314*795d594fSAndroid Build Coastguard Worker threads[i] = new Thread() { 315*795d594fSAndroid Build Coastguard Worker public void run() { 316*795d594fSAndroid Build Coastguard Worker int index = 0; 317*795d594fSAndroid Build Coastguard Worker while (!stopFlag.stop) { 318*795d594fSAndroid Build Coastguard Worker Object value = values[index]; 319*795d594fSAndroid Build Coastguard Worker index = (index + 1) & 3; 320*795d594fSAndroid Build Coastguard Worker Object nextValue = values[index]; 321*795d594fSAndroid Build Coastguard Worker boolean success = unsafeDispatch.compareAndSwapObject( 322*795d594fSAndroid Build Coastguard Worker unsafe, fourReferences, offset, value, nextValue); 323*795d594fSAndroid Build Coastguard Worker assertTrue(success); 324*795d594fSAndroid Build Coastguard Worker } 325*795d594fSAndroid Build Coastguard Worker } 326*795d594fSAndroid Build Coastguard Worker }; 327*795d594fSAndroid Build Coastguard Worker } 328*795d594fSAndroid Build Coastguard Worker // Start threads. 329*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 330*795d594fSAndroid Build Coastguard Worker threads[i].start(); 331*795d594fSAndroid Build Coastguard Worker } 332*795d594fSAndroid Build Coastguard Worker // Allocate memory to trigger some GCs 333*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 640 * 1024; ++i) { 334*795d594fSAndroid Build Coastguard Worker $noinline$allocateAtLeast1KiB(); 335*795d594fSAndroid Build Coastguard Worker } 336*795d594fSAndroid Build Coastguard Worker // Stop threads. 337*795d594fSAndroid Build Coastguard Worker stopFlag.stop = true; 338*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 339*795d594fSAndroid Build Coastguard Worker threads[i].join(); 340*795d594fSAndroid Build Coastguard Worker } 341*795d594fSAndroid Build Coastguard Worker } 342*795d594fSAndroid Build Coastguard Worker 343*795d594fSAndroid Build Coastguard Worker // Instead of using a `VarHandle` directly, this test uses `AtomicReference` which is 344*795d594fSAndroid Build Coastguard Worker // implemented using a `VarHandle`. This is because the normal `VarHandle` checks are 345*795d594fSAndroid Build Coastguard Worker // done without read barrier which makes them likely to fail and take the slow-path to 346*795d594fSAndroid Build Coastguard Worker // the runtime while the GC is marking (which is the case we're most interested in). 347*795d594fSAndroid Build Coastguard Worker // The `AtomicReference` uses a boot-image `VarHandle` which is optimized to avoid 348*795d594fSAndroid Build Coastguard Worker // those checks, making it more likely to hit bugs in the raw RMW operation. $noinline$testAtomicReference()349*795d594fSAndroid Build Coastguard Worker public static void $noinline$testAtomicReference() throws Exception { 350*795d594fSAndroid Build Coastguard Worker // Prepare `AtomicReference` object. 351*795d594fSAndroid Build Coastguard Worker // D8 rewrites the bytecode with a workaround for CAS bug. To test the raw 352*795d594fSAndroid Build Coastguard Worker // `AtomicReference.compareAndSet()` call, we implement the call in smali 353*795d594fSAndroid Build Coastguard Worker // and wrap it in an indirect call. 354*795d594fSAndroid Build Coastguard Worker final AtomicReferenceDispatch atomicReferenceDispatch = 355*795d594fSAndroid Build Coastguard Worker (AtomicReferenceDispatch) Class.forName("AtomicReferenceWrapper").newInstance(); 356*795d594fSAndroid Build Coastguard Worker final AtomicReference aref = new AtomicReference(null); 357*795d594fSAndroid Build Coastguard Worker // Prepare threads. 358*795d594fSAndroid Build Coastguard Worker final Object[] objects = new Object[] { 359*795d594fSAndroid Build Coastguard Worker null, 360*795d594fSAndroid Build Coastguard Worker new Object(), 361*795d594fSAndroid Build Coastguard Worker new Object(), 362*795d594fSAndroid Build Coastguard Worker new Object() 363*795d594fSAndroid Build Coastguard Worker }; 364*795d594fSAndroid Build Coastguard Worker final StopFlag stopFlag = new StopFlag(); 365*795d594fSAndroid Build Coastguard Worker Thread[] threads = new Thread[4]; 366*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 367*795d594fSAndroid Build Coastguard Worker if (i == 0) { 368*795d594fSAndroid Build Coastguard Worker threads[i] = new Thread() { 369*795d594fSAndroid Build Coastguard Worker public void run() { 370*795d594fSAndroid Build Coastguard Worker int index = 0; 371*795d594fSAndroid Build Coastguard Worker Object value = objects[index]; 372*795d594fSAndroid Build Coastguard Worker while (!stopFlag.stop) { 373*795d594fSAndroid Build Coastguard Worker index = (index + 1) & 3; 374*795d594fSAndroid Build Coastguard Worker Object nextValue = objects[index]; 375*795d594fSAndroid Build Coastguard Worker boolean success = atomicReferenceDispatch.compareAndSet( 376*795d594fSAndroid Build Coastguard Worker aref, value, nextValue); 377*795d594fSAndroid Build Coastguard Worker assertTrue(success); 378*795d594fSAndroid Build Coastguard Worker value = nextValue; 379*795d594fSAndroid Build Coastguard Worker } 380*795d594fSAndroid Build Coastguard Worker } 381*795d594fSAndroid Build Coastguard Worker }; 382*795d594fSAndroid Build Coastguard Worker } else { 383*795d594fSAndroid Build Coastguard Worker final Object value = objects[i]; 384*795d594fSAndroid Build Coastguard Worker assertTrue(value != null); 385*795d594fSAndroid Build Coastguard Worker threads[i] = new Thread() { 386*795d594fSAndroid Build Coastguard Worker public void run() { 387*795d594fSAndroid Build Coastguard Worker // This thread is trying to overwrite a value with the same value. 388*795d594fSAndroid Build Coastguard Worker // For a false-positive in CAS compare, it would actually change 389*795d594fSAndroid Build Coastguard Worker // the value and cause the thread `threads[0]` to fail. 390*795d594fSAndroid Build Coastguard Worker assertTrue(value != null); 391*795d594fSAndroid Build Coastguard Worker while (!stopFlag.stop) { 392*795d594fSAndroid Build Coastguard Worker // Do not check the return value. 393*795d594fSAndroid Build Coastguard Worker atomicReferenceDispatch.compareAndSet(aref, value, value); 394*795d594fSAndroid Build Coastguard Worker } 395*795d594fSAndroid Build Coastguard Worker } 396*795d594fSAndroid Build Coastguard Worker }; 397*795d594fSAndroid Build Coastguard Worker } 398*795d594fSAndroid Build Coastguard Worker }; 399*795d594fSAndroid Build Coastguard Worker // Start threads. 400*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 401*795d594fSAndroid Build Coastguard Worker threads[i].start(); 402*795d594fSAndroid Build Coastguard Worker } 403*795d594fSAndroid Build Coastguard Worker // Allocate memory to trigger some GCs 404*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 640 * 1024; ++i) { 405*795d594fSAndroid Build Coastguard Worker $noinline$allocateAtLeast1KiB(); 406*795d594fSAndroid Build Coastguard Worker } 407*795d594fSAndroid Build Coastguard Worker // Stop threads. 408*795d594fSAndroid Build Coastguard Worker stopFlag.stop = true; 409*795d594fSAndroid Build Coastguard Worker for (int i = 0; i != 4; ++i) { 410*795d594fSAndroid Build Coastguard Worker threads[i].join(); 411*795d594fSAndroid Build Coastguard Worker } 412*795d594fSAndroid Build Coastguard Worker } 413*795d594fSAndroid Build Coastguard Worker assertTrue(boolean value)414*795d594fSAndroid Build Coastguard Worker public static void assertTrue(boolean value) { 415*795d594fSAndroid Build Coastguard Worker if (!value) { 416*795d594fSAndroid Build Coastguard Worker throw new Error("Assertion failed!"); 417*795d594fSAndroid Build Coastguard Worker } 418*795d594fSAndroid Build Coastguard Worker } 419*795d594fSAndroid Build Coastguard Worker getUnsafe()420*795d594fSAndroid Build Coastguard Worker public static Unsafe getUnsafe() throws Exception { 421*795d594fSAndroid Build Coastguard Worker Class<?> unsafeClass = Class.forName("sun.misc.Unsafe"); 422*795d594fSAndroid Build Coastguard Worker Field f = unsafeClass.getDeclaredField("theUnsafe"); 423*795d594fSAndroid Build Coastguard Worker f.setAccessible(true); 424*795d594fSAndroid Build Coastguard Worker return (Unsafe) f.get(null); 425*795d594fSAndroid Build Coastguard Worker } 426*795d594fSAndroid Build Coastguard Worker $noinline$allocateAtLeast1KiB()427*795d594fSAndroid Build Coastguard Worker public static void $noinline$allocateAtLeast1KiB() { 428*795d594fSAndroid Build Coastguard Worker // Give GC more work by allocating Object arrays. 429*795d594fSAndroid Build Coastguard Worker memory[allocationIndex] = new Object[1024 / 4]; 430*795d594fSAndroid Build Coastguard Worker ++allocationIndex; 431*795d594fSAndroid Build Coastguard Worker if (allocationIndex == memory.length) { 432*795d594fSAndroid Build Coastguard Worker allocationIndex = 0; 433*795d594fSAndroid Build Coastguard Worker } 434*795d594fSAndroid Build Coastguard Worker } 435*795d594fSAndroid Build Coastguard Worker 436*795d594fSAndroid Build Coastguard Worker // We shall retain some allocated memory and release old allocations 437*795d594fSAndroid Build Coastguard Worker // so that the GC has something to do. 438*795d594fSAndroid Build Coastguard Worker public static Object[] memory = new Object[1024]; 439*795d594fSAndroid Build Coastguard Worker public static int allocationIndex = 0; 440*795d594fSAndroid Build Coastguard Worker } 441*795d594fSAndroid Build Coastguard Worker 442*795d594fSAndroid Build Coastguard Worker class StopFlag { 443*795d594fSAndroid Build Coastguard Worker public volatile boolean stop = false; 444*795d594fSAndroid Build Coastguard Worker } 445*795d594fSAndroid Build Coastguard Worker 446*795d594fSAndroid Build Coastguard Worker class FourBytes { 447*795d594fSAndroid Build Coastguard Worker public byte b1 = (byte) 0; 448*795d594fSAndroid Build Coastguard Worker public byte b2 = (byte) 0; 449*795d594fSAndroid Build Coastguard Worker public byte b3 = (byte) 0; 450*795d594fSAndroid Build Coastguard Worker public byte b4 = (byte) 0; 451*795d594fSAndroid Build Coastguard Worker } 452*795d594fSAndroid Build Coastguard Worker 453*795d594fSAndroid Build Coastguard Worker class FourInts { 454*795d594fSAndroid Build Coastguard Worker public int i1 = 0; 455*795d594fSAndroid Build Coastguard Worker public int i2 = 0; 456*795d594fSAndroid Build Coastguard Worker public int i3 = 0; 457*795d594fSAndroid Build Coastguard Worker public int i4 = 0; 458*795d594fSAndroid Build Coastguard Worker } 459*795d594fSAndroid Build Coastguard Worker 460*795d594fSAndroid Build Coastguard Worker class FourLongs { 461*795d594fSAndroid Build Coastguard Worker public long l1 = 0L; 462*795d594fSAndroid Build Coastguard Worker public long l2 = 0L; 463*795d594fSAndroid Build Coastguard Worker public long l3 = 0L; 464*795d594fSAndroid Build Coastguard Worker public long l4 = 0L; 465*795d594fSAndroid Build Coastguard Worker } 466*795d594fSAndroid Build Coastguard Worker 467*795d594fSAndroid Build Coastguard Worker class FourReferences { 468*795d594fSAndroid Build Coastguard Worker public Object r1 = null; 469*795d594fSAndroid Build Coastguard Worker public Object r2 = null; 470*795d594fSAndroid Build Coastguard Worker public Object r3 = null; 471*795d594fSAndroid Build Coastguard Worker public Object r4 = null; 472*795d594fSAndroid Build Coastguard Worker } 473*795d594fSAndroid Build Coastguard Worker 474*795d594fSAndroid Build Coastguard Worker abstract class UnsafeDispatch { compareAndSwapObject( Unsafe unsafe, Object obj, long offset, Object expected, Object new_value)475*795d594fSAndroid Build Coastguard Worker public abstract boolean compareAndSwapObject( 476*795d594fSAndroid Build Coastguard Worker Unsafe unsafe, Object obj, long offset, Object expected, Object new_value); 477*795d594fSAndroid Build Coastguard Worker } 478*795d594fSAndroid Build Coastguard Worker 479*795d594fSAndroid Build Coastguard Worker abstract class AtomicReferenceDispatch { compareAndSet(AtomicReference aref, Object expected, Object new_value)480*795d594fSAndroid Build Coastguard Worker public abstract boolean compareAndSet(AtomicReference aref, Object expected, Object new_value); 481*795d594fSAndroid Build Coastguard Worker } 482