xref: /aosp_15_r20/art/test/183-rmw-stress-test/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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