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