xref: /aosp_15_r20/art/test/2029-contended-monitors/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2019 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 import java.util.concurrent.atomic.AtomicInteger;
17*795d594fSAndroid Build Coastguard Worker 
18*795d594fSAndroid Build Coastguard Worker public class Main {
19*795d594fSAndroid Build Coastguard Worker 
20*795d594fSAndroid Build Coastguard Worker   private final boolean PRINT_TIMES = false;  // False for use as run test.
21*795d594fSAndroid Build Coastguard Worker 
22*795d594fSAndroid Build Coastguard Worker   // Number of increments done by each thread.  Must be multiple of largest hold time below,
23*795d594fSAndroid Build Coastguard Worker   // times any possible thread count. We reduce this below if PRINT_TIMES is not set, and
24*795d594fSAndroid Build Coastguard Worker   // even more in debuggable mode.
25*795d594fSAndroid Build Coastguard Worker   private final int UNSCALED_TOTAL_ITERS  = 16_000_000;
26*795d594fSAndroid Build Coastguard Worker   private final int UNSCALED_MAX_HOLD_TIME = 2_000_000;
27*795d594fSAndroid Build Coastguard Worker   private int totalIters;
28*795d594fSAndroid Build Coastguard Worker   private int maxHoldTime;
29*795d594fSAndroid Build Coastguard Worker 
30*795d594fSAndroid Build Coastguard Worker   private int counter;
31*795d594fSAndroid Build Coastguard Worker 
32*795d594fSAndroid Build Coastguard Worker   private AtomicInteger atomicCounter = new AtomicInteger();
33*795d594fSAndroid Build Coastguard Worker 
34*795d594fSAndroid Build Coastguard Worker   private Object lock;
35*795d594fSAndroid Build Coastguard Worker 
36*795d594fSAndroid Build Coastguard Worker   private int currentThreadCount = 0;
37*795d594fSAndroid Build Coastguard Worker 
38*795d594fSAndroid Build Coastguard Worker   private static boolean debuggable = false;  // Running in a debuggable ART environment.
39*795d594fSAndroid Build Coastguard Worker 
40*795d594fSAndroid Build Coastguard Worker   // A function such that if we repeatedly apply it to -1, the value oscillates
41*795d594fSAndroid Build Coastguard Worker   // between -1 and 3. Thus the average value is 1.
42*795d594fSAndroid Build Coastguard Worker   // This is designed to make it hard for the compiler to predict the values in
43*795d594fSAndroid Build Coastguard Worker   // the sequence.
nextInt(int x)44*795d594fSAndroid Build Coastguard Worker   private int nextInt(int x) {
45*795d594fSAndroid Build Coastguard Worker     if (x < 0) {
46*795d594fSAndroid Build Coastguard Worker       return x * x + 2;
47*795d594fSAndroid Build Coastguard Worker     } else {
48*795d594fSAndroid Build Coastguard Worker       return x - 4;
49*795d594fSAndroid Build Coastguard Worker     }
50*795d594fSAndroid Build Coastguard Worker   }
51*795d594fSAndroid Build Coastguard Worker 
52*795d594fSAndroid Build Coastguard Worker   // Increment counter by n, holding lock for time roughly propertional to n.
53*795d594fSAndroid Build Coastguard Worker   // N must be even.
holdFor(Object lock, int n)54*795d594fSAndroid Build Coastguard Worker   private void holdFor(Object lock, int n) {
55*795d594fSAndroid Build Coastguard Worker     synchronized(lock) {
56*795d594fSAndroid Build Coastguard Worker       int y = -1;
57*795d594fSAndroid Build Coastguard Worker       for (int i = 0; i < n; ++i) {
58*795d594fSAndroid Build Coastguard Worker         counter += y;
59*795d594fSAndroid Build Coastguard Worker         y = nextInt(y);
60*795d594fSAndroid Build Coastguard Worker       }
61*795d594fSAndroid Build Coastguard Worker     }
62*795d594fSAndroid Build Coastguard Worker   }
63*795d594fSAndroid Build Coastguard Worker 
64*795d594fSAndroid Build Coastguard Worker   // Increment local by an even number n in a way that takes time roughly proportional
65*795d594fSAndroid Build Coastguard Worker   // to n.
spinFor(int n)66*795d594fSAndroid Build Coastguard Worker   private void spinFor(int n) {
67*795d594fSAndroid Build Coastguard Worker     int y = -1;
68*795d594fSAndroid Build Coastguard Worker     int local_counter = 0;
69*795d594fSAndroid Build Coastguard Worker     for (int i = 0; i < n; ++i) {
70*795d594fSAndroid Build Coastguard Worker       local_counter += y;
71*795d594fSAndroid Build Coastguard Worker       y = nextInt(y);
72*795d594fSAndroid Build Coastguard Worker     }
73*795d594fSAndroid Build Coastguard Worker     if (local_counter != n) {
74*795d594fSAndroid Build Coastguard Worker       throw new Error();
75*795d594fSAndroid Build Coastguard Worker     }
76*795d594fSAndroid Build Coastguard Worker   }
77*795d594fSAndroid Build Coastguard Worker 
78*795d594fSAndroid Build Coastguard Worker   private class RepeatedLockHolder implements Runnable {
RepeatedLockHolder(boolean shared, int n )79*795d594fSAndroid Build Coastguard Worker     RepeatedLockHolder(boolean shared, int n /* even */) {
80*795d594fSAndroid Build Coastguard Worker       sharedLock = shared;
81*795d594fSAndroid Build Coastguard Worker       holdTime = n;
82*795d594fSAndroid Build Coastguard Worker     }
83*795d594fSAndroid Build Coastguard Worker     @Override
run()84*795d594fSAndroid Build Coastguard Worker     public void run() {
85*795d594fSAndroid Build Coastguard Worker       Object myLock = sharedLock ? lock : new Object();
86*795d594fSAndroid Build Coastguard Worker       int nIters = totalIters / currentThreadCount / holdTime;
87*795d594fSAndroid Build Coastguard Worker       for (int i = 0; i < nIters; ++i) {
88*795d594fSAndroid Build Coastguard Worker         holdFor(myLock, holdTime);
89*795d594fSAndroid Build Coastguard Worker       }
90*795d594fSAndroid Build Coastguard Worker     }
91*795d594fSAndroid Build Coastguard Worker     private boolean sharedLock;
92*795d594fSAndroid Build Coastguard Worker     private int holdTime;
93*795d594fSAndroid Build Coastguard Worker   }
94*795d594fSAndroid Build Coastguard Worker 
95*795d594fSAndroid Build Coastguard Worker   private class RepeatedIntermittentLockHolder implements Runnable {
RepeatedIntermittentLockHolder(boolean shared, int n )96*795d594fSAndroid Build Coastguard Worker     RepeatedIntermittentLockHolder(boolean shared, int n /* even */) {
97*795d594fSAndroid Build Coastguard Worker       sharedLock = shared;
98*795d594fSAndroid Build Coastguard Worker       holdTime = n;
99*795d594fSAndroid Build Coastguard Worker     }
100*795d594fSAndroid Build Coastguard Worker     @Override
run()101*795d594fSAndroid Build Coastguard Worker     public void run() {
102*795d594fSAndroid Build Coastguard Worker       Object myLock = sharedLock ? lock : new Object();
103*795d594fSAndroid Build Coastguard Worker       int iter_divisor = 10 * currentThreadCount * holdTime;
104*795d594fSAndroid Build Coastguard Worker       int nIters = totalIters / iter_divisor;
105*795d594fSAndroid Build Coastguard Worker       if (totalIters % iter_divisor != 0 || holdTime % 2 == 1) {
106*795d594fSAndroid Build Coastguard Worker         System.err.println("Misconfigured: totalIters = " + totalIters
107*795d594fSAndroid Build Coastguard Worker             + " iter_divisor = "  + iter_divisor);
108*795d594fSAndroid Build Coastguard Worker       }
109*795d594fSAndroid Build Coastguard Worker       for (int i = 0; i < nIters; ++i) {
110*795d594fSAndroid Build Coastguard Worker         spinFor(9 * holdTime);
111*795d594fSAndroid Build Coastguard Worker         holdFor(myLock, holdTime);
112*795d594fSAndroid Build Coastguard Worker       }
113*795d594fSAndroid Build Coastguard Worker     }
114*795d594fSAndroid Build Coastguard Worker     private boolean sharedLock;
115*795d594fSAndroid Build Coastguard Worker     private int holdTime;
116*795d594fSAndroid Build Coastguard Worker   }
117*795d594fSAndroid Build Coastguard Worker 
118*795d594fSAndroid Build Coastguard Worker   private class SleepyLockHolder implements Runnable {
SleepyLockHolder(boolean shared)119*795d594fSAndroid Build Coastguard Worker     SleepyLockHolder(boolean shared) {
120*795d594fSAndroid Build Coastguard Worker       sharedLock = shared;
121*795d594fSAndroid Build Coastguard Worker     }
122*795d594fSAndroid Build Coastguard Worker     @Override
run()123*795d594fSAndroid Build Coastguard Worker     public void run() {
124*795d594fSAndroid Build Coastguard Worker       Object myLock = sharedLock ? lock : new Object();
125*795d594fSAndroid Build Coastguard Worker       int nIters = totalIters / currentThreadCount / 10_000;
126*795d594fSAndroid Build Coastguard Worker       for (int i = 0; i < nIters; ++i) {
127*795d594fSAndroid Build Coastguard Worker         synchronized(myLock) {
128*795d594fSAndroid Build Coastguard Worker           try {
129*795d594fSAndroid Build Coastguard Worker             Thread.sleep(2);
130*795d594fSAndroid Build Coastguard Worker           } catch(InterruptedException e) {
131*795d594fSAndroid Build Coastguard Worker             throw new AssertionError("Unexpected interrupt");
132*795d594fSAndroid Build Coastguard Worker           }
133*795d594fSAndroid Build Coastguard Worker           counter += 10_000;
134*795d594fSAndroid Build Coastguard Worker         }
135*795d594fSAndroid Build Coastguard Worker       }
136*795d594fSAndroid Build Coastguard Worker     }
137*795d594fSAndroid Build Coastguard Worker     private boolean sharedLock;
138*795d594fSAndroid Build Coastguard Worker   }
139*795d594fSAndroid Build Coastguard Worker 
140*795d594fSAndroid Build Coastguard Worker   // Increment atomicCounter n times, on average by 1 each time.
141*795d594fSAndroid Build Coastguard Worker   private class RepeatedIncrementer implements Runnable {
142*795d594fSAndroid Build Coastguard Worker     @Override
run()143*795d594fSAndroid Build Coastguard Worker     public void run() {
144*795d594fSAndroid Build Coastguard Worker       int y = -1;
145*795d594fSAndroid Build Coastguard Worker       int nIters = totalIters / currentThreadCount;
146*795d594fSAndroid Build Coastguard Worker       for (int i = 0; i < nIters; ++i) {
147*795d594fSAndroid Build Coastguard Worker         atomicCounter.addAndGet(y);
148*795d594fSAndroid Build Coastguard Worker         y = nextInt(y);
149*795d594fSAndroid Build Coastguard Worker       }
150*795d594fSAndroid Build Coastguard Worker     }
151*795d594fSAndroid Build Coastguard Worker   }
152*795d594fSAndroid Build Coastguard Worker 
153*795d594fSAndroid Build Coastguard Worker   // Run n threads doing work. Return the elapsed time this took, in milliseconds.
runMultiple(int n, Runnable work)154*795d594fSAndroid Build Coastguard Worker   private long runMultiple(int n, Runnable work) {
155*795d594fSAndroid Build Coastguard Worker     Thread[] threads = new Thread[n];
156*795d594fSAndroid Build Coastguard Worker     // Replace lock, so that we start with a clean, uninflated lock each time.
157*795d594fSAndroid Build Coastguard Worker     lock = new Object();
158*795d594fSAndroid Build Coastguard Worker     for (int i = 0; i < n; ++i) {
159*795d594fSAndroid Build Coastguard Worker       threads[i] = new Thread(work);
160*795d594fSAndroid Build Coastguard Worker     }
161*795d594fSAndroid Build Coastguard Worker     long startTime = System.currentTimeMillis();
162*795d594fSAndroid Build Coastguard Worker     for (int i = 0; i < n; ++i) {
163*795d594fSAndroid Build Coastguard Worker       threads[i].start();
164*795d594fSAndroid Build Coastguard Worker     }
165*795d594fSAndroid Build Coastguard Worker     for (int i = 0; i < n; ++i) {
166*795d594fSAndroid Build Coastguard Worker       try {
167*795d594fSAndroid Build Coastguard Worker         threads[i].join();
168*795d594fSAndroid Build Coastguard Worker       } catch(InterruptedException e) {
169*795d594fSAndroid Build Coastguard Worker         throw new AssertionError("Unexpected interrupt");
170*795d594fSAndroid Build Coastguard Worker       }
171*795d594fSAndroid Build Coastguard Worker     }
172*795d594fSAndroid Build Coastguard Worker     return System.currentTimeMillis() - startTime;
173*795d594fSAndroid Build Coastguard Worker   }
174*795d594fSAndroid Build Coastguard Worker 
175*795d594fSAndroid Build Coastguard Worker   // Run on different numbers of threads.
runAll(Runnable work, Runnable init, Runnable checker)176*795d594fSAndroid Build Coastguard Worker   private void runAll(Runnable work, Runnable init, Runnable checker) {
177*795d594fSAndroid Build Coastguard Worker     for (int i = 1; i <= 8; i *= 2) {
178*795d594fSAndroid Build Coastguard Worker       currentThreadCount = i;
179*795d594fSAndroid Build Coastguard Worker       init.run();
180*795d594fSAndroid Build Coastguard Worker       long time = runMultiple(i, work);
181*795d594fSAndroid Build Coastguard Worker       if (PRINT_TIMES) {
182*795d594fSAndroid Build Coastguard Worker         System.out.print(time + (i == 8 ? "\n" : "\t"));
183*795d594fSAndroid Build Coastguard Worker       }
184*795d594fSAndroid Build Coastguard Worker       checker.run();
185*795d594fSAndroid Build Coastguard Worker     }
186*795d594fSAndroid Build Coastguard Worker   }
187*795d594fSAndroid Build Coastguard Worker 
188*795d594fSAndroid Build Coastguard Worker   private class CheckAtomicCounter implements Runnable {
189*795d594fSAndroid Build Coastguard Worker     @Override
run()190*795d594fSAndroid Build Coastguard Worker     public void run() {
191*795d594fSAndroid Build Coastguard Worker       if (atomicCounter.get() != totalIters) {
192*795d594fSAndroid Build Coastguard Worker         throw new AssertionError("Failed atomicCounter postcondition check for "
193*795d594fSAndroid Build Coastguard Worker             + currentThreadCount + " threads");
194*795d594fSAndroid Build Coastguard Worker       }
195*795d594fSAndroid Build Coastguard Worker     }
196*795d594fSAndroid Build Coastguard Worker   }
197*795d594fSAndroid Build Coastguard Worker 
198*795d594fSAndroid Build Coastguard Worker   private class CheckCounter implements Runnable {
199*795d594fSAndroid Build Coastguard Worker     private final int expected;
CheckCounter(int e)200*795d594fSAndroid Build Coastguard Worker     public CheckCounter(int e) {
201*795d594fSAndroid Build Coastguard Worker       expected = e;
202*795d594fSAndroid Build Coastguard Worker     }
203*795d594fSAndroid Build Coastguard Worker     @Override
run()204*795d594fSAndroid Build Coastguard Worker     public void run() {
205*795d594fSAndroid Build Coastguard Worker       if (counter != expected) {
206*795d594fSAndroid Build Coastguard Worker         throw new AssertionError("Failed counter postcondition check for "
207*795d594fSAndroid Build Coastguard Worker             + currentThreadCount + " threads, expected " + expected + " got " + counter);
208*795d594fSAndroid Build Coastguard Worker       }
209*795d594fSAndroid Build Coastguard Worker     }
210*795d594fSAndroid Build Coastguard Worker   }
211*795d594fSAndroid Build Coastguard Worker 
run()212*795d594fSAndroid Build Coastguard Worker   private void run() {
213*795d594fSAndroid Build Coastguard Worker     int scale = PRINT_TIMES ? 1 : (debuggable ? 20 : 10);
214*795d594fSAndroid Build Coastguard Worker     totalIters = UNSCALED_TOTAL_ITERS / scale;
215*795d594fSAndroid Build Coastguard Worker     maxHoldTime = UNSCALED_MAX_HOLD_TIME / scale;
216*795d594fSAndroid Build Coastguard Worker     if (PRINT_TIMES) {
217*795d594fSAndroid Build Coastguard Worker       System.out.println("All times in milliseconds for 1, 2, 4 and 8 threads");
218*795d594fSAndroid Build Coastguard Worker     }
219*795d594fSAndroid Build Coastguard Worker     System.out.println("Atomic increments");
220*795d594fSAndroid Build Coastguard Worker     runAll(new RepeatedIncrementer(), () -> { atomicCounter.set(0); }, new CheckAtomicCounter());
221*795d594fSAndroid Build Coastguard Worker     for (int i = 2; i <= UNSCALED_MAX_HOLD_TIME / 100; i *= 10) {
222*795d594fSAndroid Build Coastguard Worker       // i * 8 (max thread count) divides totalIters
223*795d594fSAndroid Build Coastguard Worker       System.out.println("Hold time " + i + ", shared lock");
224*795d594fSAndroid Build Coastguard Worker       runAll(new RepeatedLockHolder(true, i), () -> { counter = 0; },
225*795d594fSAndroid Build Coastguard Worker           new CheckCounter(totalIters));
226*795d594fSAndroid Build Coastguard Worker     }
227*795d594fSAndroid Build Coastguard Worker     for (int i = 2; i <= UNSCALED_MAX_HOLD_TIME / 1000; i *= 10) {
228*795d594fSAndroid Build Coastguard Worker       // i * 8 (max thread count) divides totalIters
229*795d594fSAndroid Build Coastguard Worker       System.out.println("Hold time " + i + ", pause time " + (9 * i) + ", shared lock");
230*795d594fSAndroid Build Coastguard Worker       runAll(new RepeatedIntermittentLockHolder(true, i), () -> { counter = 0; },
231*795d594fSAndroid Build Coastguard Worker           new CheckCounter(totalIters / 10));
232*795d594fSAndroid Build Coastguard Worker     }
233*795d594fSAndroid Build Coastguard Worker     if (PRINT_TIMES) {
234*795d594fSAndroid Build Coastguard Worker       for (int i = 2; i <= maxHoldTime; i *= 1000) {
235*795d594fSAndroid Build Coastguard Worker         // i divides totalIters
236*795d594fSAndroid Build Coastguard Worker         System.out.println("Hold time " + i + ", private lock");
237*795d594fSAndroid Build Coastguard Worker         // Since there is no mutual exclusion final counter value is unpredictable.
238*795d594fSAndroid Build Coastguard Worker         runAll(new RepeatedLockHolder(false, i), () -> { counter = 0; }, () -> {});
239*795d594fSAndroid Build Coastguard Worker       }
240*795d594fSAndroid Build Coastguard Worker     }
241*795d594fSAndroid Build Coastguard Worker     System.out.println("Hold for 2 msecs while sleeping, shared lock");
242*795d594fSAndroid Build Coastguard Worker     runAll(new SleepyLockHolder(true), () -> { counter = 0; }, new CheckCounter(totalIters));
243*795d594fSAndroid Build Coastguard Worker     System.out.println("Hold for 2 msecs while sleeping, private lock");
244*795d594fSAndroid Build Coastguard Worker     runAll(new SleepyLockHolder(false), () -> { counter = 0; }, () -> {});
245*795d594fSAndroid Build Coastguard Worker   }
246*795d594fSAndroid Build Coastguard Worker 
main(String[] args)247*795d594fSAndroid Build Coastguard Worker   public static void main(String[] args) {
248*795d594fSAndroid Build Coastguard Worker     if (System.getProperty("java.vm.name").equalsIgnoreCase("Dalvik")) {
249*795d594fSAndroid Build Coastguard Worker       try {
250*795d594fSAndroid Build Coastguard Worker         System.loadLibrary(args[0]);
251*795d594fSAndroid Build Coastguard Worker         debuggable = isDebuggable();
252*795d594fSAndroid Build Coastguard Worker       } catch (Throwable t) {
253*795d594fSAndroid Build Coastguard Worker         // Conservatively assume we might be debuggable, and pretend we loaded the library.
254*795d594fSAndroid Build Coastguard Worker         // As of June 2024, we seem to get here with atest.
255*795d594fSAndroid Build Coastguard Worker         debuggable = true;
256*795d594fSAndroid Build Coastguard Worker         System.out.println("JNI_OnLoad called");
257*795d594fSAndroid Build Coastguard Worker       }
258*795d594fSAndroid Build Coastguard Worker     } else {
259*795d594fSAndroid Build Coastguard Worker       System.out.println("JNI_OnLoad called");  // Not really, but match expected output.
260*795d594fSAndroid Build Coastguard Worker     }
261*795d594fSAndroid Build Coastguard Worker     System.out.println("Starting");
262*795d594fSAndroid Build Coastguard Worker     new Main().run();
263*795d594fSAndroid Build Coastguard Worker   }
264*795d594fSAndroid Build Coastguard Worker 
isDebuggable()265*795d594fSAndroid Build Coastguard Worker   private static native boolean isDebuggable();
266*795d594fSAndroid Build Coastguard Worker }
267