| /* |
| * 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."); |
| } |
| } |