blob: b1fb841c30195ba2c6df9c59e596d0ae2caadb62 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package art;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.LockSupport;
import java.util.ListIterator;
import java.util.function.Consumer;
import java.util.function.Function;
public class Test1931 {
private static Monitors.LockController CONTENTION_SUPPRESSED = null;
public static AutoCloseable SuppressContention(Monitors.LockController controller) {
if (CONTENTION_SUPPRESSED != null) {
throw new IllegalStateException("Only one contention suppression is possible at a time.");
}
CONTENTION_SUPPRESSED = controller;
return () -> {
CONTENTION_SUPPRESSED = null;
};
}
public static void printStackTrace(Throwable t) {
System.out.println("Caught exception: " + t);
for (Throwable c = t.getCause(); c != null; c = c.getCause()) {
System.out.println("\tCaused by: " +
(Test1931.class.getPackage().equals(c.getClass().getPackage())
? c.toString() : c.getClass().toString()));
}
}
public static void handleMonitorEnter(Thread thd, Object lock) {
if (CONTENTION_SUPPRESSED != null && CONTENTION_SUPPRESSED.IsWorkerThread(thd)) {
return;
}
System.out.println(thd.getName() + " contended-LOCKING " + lock);
}
public static void handleMonitorEntered(Thread thd, Object lock) {
if (CONTENTION_SUPPRESSED != null && CONTENTION_SUPPRESSED.IsWorkerThread(thd)) {
return;
}
System.out.println(thd.getName() + " LOCKED " + lock);
}
public static void handleMonitorWait(Thread thd, Object lock, long timeout) {
System.out.println(thd.getName() + " start-monitor-wait " + lock + " timeout: " + timeout);
}
public static void handleMonitorWaited(Thread thd, Object lock, boolean timed_out) {
System.out.println(thd.getName() + " monitor-waited " + lock + " timed_out: " + timed_out);
}
public static void run() throws Exception {
Monitors.setupMonitorEvents(
Test1931.class,
Test1931.class.getDeclaredMethod("handleMonitorEnter", Thread.class, Object.class),
Test1931.class.getDeclaredMethod("handleMonitorEntered", Thread.class, Object.class),
Test1931.class.getDeclaredMethod("handleMonitorWait",
Thread.class, Object.class, Long.TYPE),
Test1931.class.getDeclaredMethod("handleMonitorWaited",
Thread.class, Object.class, Boolean.TYPE),
Monitors.NamedLock.class,
null);
System.out.println("Testing contended locking.");
testLock(new Monitors.NamedLock("Lock testLock"));
System.out.println("Testing park.");
testPark(new Monitors.NamedLock("Parking blocker object"));
System.out.println("Testing monitor wait.");
testWait(new Monitors.NamedLock("Lock testWait"));
System.out.println("Testing monitor timed wait.");
testTimedWait(new Monitors.NamedLock("Lock testTimedWait"));
System.out.println("Testing monitor timed with timeout.");
testTimedWaitTimeout(new Monitors.NamedLock("Lock testTimedWaitTimeout"));
// TODO It would be good (but annoying) to do this with jasmin/smali in order to test if it's
// different without the reflection.
System.out.println("Waiting on an unlocked monitor.");
testUnlockedWait(new Monitors.NamedLock("Lock testUnlockedWait"));
System.out.println("Waiting with an illegal argument (negative timeout)");
testIllegalWait(new Monitors.NamedLock("Lock testIllegalWait"));
System.out.println("Interrupt a monitor being waited on.");
testInteruptWait(new Monitors.NamedLock("Lock testInteruptWait"));
}
public static void testPark(Object blocker) throws Exception {
Thread holder = new Thread(() -> {
LockSupport.parkNanos(blocker, 10); // Should round up to one millisecond
}, "ParkThread");
holder.start();
holder.join();
}
public static void testInteruptWait(final Monitors.NamedLock lk) throws Exception {
final Monitors.LockController controller1 = new Monitors.LockController(lk);
controller1.DoLock();
controller1.waitForLockToBeHeld();
controller1.DoWait();
controller1.waitForNotifySleep();
try {
controller1.interruptWorker();
controller1.waitForLockToBeHeld();
controller1.DoUnlock();
System.out.println("No Exception thrown!");
} catch (Monitors.TestException e) {
printStackTrace(e);
}
controller1.DoCleanup();
}
public static void testIllegalWait(final Monitors.NamedLock lk) throws Exception {
Monitors.LockController controller1 = new Monitors.LockController(lk, /*timed_wait time*/-100);
controller1.DoLock();
controller1.waitForLockToBeHeld();
try {
controller1.DoTimedWait();
controller1.waitForNotifySleep();
controller1.waitForLockToBeHeld();
controller1.DoUnlock();
System.out.println("No Exception thrown!");
} catch (Monitors.TestException e) {
printStackTrace(e);
}
controller1.DoCleanup();
}
public static void testUnlockedWait(final Monitors.NamedLock lk) throws Exception {
synchronized (lk) {
Thread thd = new Thread(() -> {
try {
Method m = Object.class.getDeclaredMethod("wait");
m.invoke(lk);
} catch (Exception e) {
printStackTrace(e);
}
}, "Unlocked wait thread:");
thd.start();
thd.join();
}
}
public static void testLock(Monitors.NamedLock lk) throws Exception {
Monitors.LockController controller1 = new Monitors.LockController(lk);
Monitors.LockController controller2 = new Monitors.LockController(lk);
controller1.DoLock();
controller1.waitForLockToBeHeld();
controller2.DoLock();
if (controller2.IsLocked()) {
throw new Exception("c2 was able to gain lock while it was held by c1");
}
controller2.waitForContendedSleep();
controller1.DoUnlock();
controller2.waitForLockToBeHeld();
controller2.DoUnlock();
}
public static void testWait(Monitors.NamedLock lk) throws Exception {
Monitors.LockController controller1 = new Monitors.LockController(lk);
Monitors.LockController controller2 = new Monitors.LockController(lk);
controller1.DoLock();
controller1.waitForLockToBeHeld();
controller1.DoWait();
controller1.waitForNotifySleep();
try (AutoCloseable suppress = SuppressContention(controller2)) {
// If controller1 has a spurious wakeup we could see contention here. Suppress it so it won't
// cause the test to fail.
controller2.DoLock();
controller2.waitForLockToBeHeld();
controller2.DoNotifyAll();
controller2.DoUnlock();
}
controller1.waitForLockToBeHeld();
controller1.DoUnlock();
}
public static void testTimedWait(Monitors.NamedLock lk) throws Exception {
// Time to wait (1 hour). We will wake it up before timeout.
final long millis = 60l * 60l * 1000l;
Monitors.LockController controller1 = new Monitors.LockController(lk, millis);
Monitors.LockController controller2 = new Monitors.LockController(lk);
controller1.DoLock();
controller1.waitForLockToBeHeld();
controller1.DoTimedWait();
controller1.waitForNotifySleep();
try (AutoCloseable suppress = SuppressContention(controller2)) {
// If controller1 has a spurious wakeup we could see contention here. Suppress it so it won't
// cause the test to fail.
controller2.DoLock();
controller2.waitForLockToBeHeld();
controller2.DoNotifyAll();
controller2.DoUnlock();
}
controller1.waitForLockToBeHeld();
controller1.DoUnlock();
}
public static void testTimedWaitTimeout(Monitors.NamedLock lk) throws Exception {
// Time to wait (10 seconds). We will wait for the timeout.
final long millis = 10l * 1000l;
Monitors.LockController controller1 = new Monitors.LockController(lk, millis);
controller1.DoLock();
controller1.waitForLockToBeHeld();
System.out.println("Waiting for 10 seconds.");
controller1.DoTimedWait();
controller1.waitForNotifySleep();
controller1.DoUnlock();
System.out.println("Wait finished with timeout.");
}
}