1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package art; 18 19 import java.lang.reflect.Method; 20 import java.util.Arrays; 21 import java.util.Objects; 22 import java.util.HashMap; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.concurrent.atomic.*; 26 import java.util.concurrent.locks.LockSupport; 27 import java.util.ListIterator; 28 import java.util.function.Consumer; 29 import java.util.function.Function; 30 31 public class Test1931 { 32 private static Monitors.LockController CONTENTION_SUPPRESSED = null; 33 SuppressContention(Monitors.LockController controller)34 public static AutoCloseable SuppressContention(Monitors.LockController controller) { 35 if (CONTENTION_SUPPRESSED != null) { 36 throw new IllegalStateException("Only one contention suppression is possible at a time."); 37 } 38 CONTENTION_SUPPRESSED = controller; 39 return () -> { 40 CONTENTION_SUPPRESSED = null; 41 }; 42 } 43 printStackTrace(Throwable t)44 public static void printStackTrace(Throwable t) { 45 System.out.println("Caught exception: " + t); 46 for (Throwable c = t.getCause(); c != null; c = c.getCause()) { 47 System.out.println("\tCaused by: " + 48 (Test1931.class.getPackage().equals(c.getClass().getPackage()) 49 ? c.toString() : c.getClass().toString())); 50 } 51 } 52 handleMonitorEnter(Thread thd, Object lock)53 public static void handleMonitorEnter(Thread thd, Object lock) { 54 if (CONTENTION_SUPPRESSED != null && CONTENTION_SUPPRESSED.IsWorkerThread(thd)) { 55 return; 56 } 57 System.out.println(thd.getName() + " contended-LOCKING " + lock); 58 } 59 handleMonitorEntered(Thread thd, Object lock)60 public static void handleMonitorEntered(Thread thd, Object lock) { 61 if (CONTENTION_SUPPRESSED != null && CONTENTION_SUPPRESSED.IsWorkerThread(thd)) { 62 return; 63 } 64 System.out.println(thd.getName() + " LOCKED " + lock); 65 } handleMonitorWait(Thread thd, Object lock, long timeout)66 public static void handleMonitorWait(Thread thd, Object lock, long timeout) { 67 System.out.println(thd.getName() + " start-monitor-wait " + lock + " timeout: " + timeout); 68 } 69 handleMonitorWaited(Thread thd, Object lock, boolean timed_out)70 public static void handleMonitorWaited(Thread thd, Object lock, boolean timed_out) { 71 System.out.println(thd.getName() + " monitor-waited " + lock + " timed_out: " + timed_out); 72 } 73 run()74 public static void run() throws Exception { 75 Monitors.setupMonitorEvents( 76 Test1931.class, 77 Test1931.class.getDeclaredMethod("handleMonitorEnter", Thread.class, Object.class), 78 Test1931.class.getDeclaredMethod("handleMonitorEntered", Thread.class, Object.class), 79 Test1931.class.getDeclaredMethod("handleMonitorWait", 80 Thread.class, Object.class, Long.TYPE), 81 Test1931.class.getDeclaredMethod("handleMonitorWaited", 82 Thread.class, Object.class, Boolean.TYPE), 83 Monitors.NamedLock.class, 84 null); 85 86 System.out.println("Testing contended locking."); 87 testLock(new Monitors.NamedLock("Lock testLock")); 88 89 System.out.println("Testing park."); 90 testPark(new Monitors.NamedLock("Parking blocker object")); 91 92 System.out.println("Testing monitor wait."); 93 testWait(new Monitors.NamedLock("Lock testWait")); 94 95 System.out.println("Testing monitor timed wait."); 96 testTimedWait(new Monitors.NamedLock("Lock testTimedWait")); 97 98 System.out.println("Testing monitor timed with timeout."); 99 testTimedWaitTimeout(new Monitors.NamedLock("Lock testTimedWaitTimeout")); 100 101 // TODO It would be good (but annoying) to do this with jasmin/smali in order to test if it's 102 // different without the reflection. 103 System.out.println("Waiting on an unlocked monitor."); 104 testUnlockedWait(new Monitors.NamedLock("Lock testUnlockedWait")); 105 106 System.out.println("Waiting with an illegal argument (negative timeout)"); 107 testIllegalWait(new Monitors.NamedLock("Lock testIllegalWait")); 108 109 System.out.println("Interrupt a monitor being waited on."); 110 testInteruptWait(new Monitors.NamedLock("Lock testInteruptWait")); 111 } 112 testPark(Object blocker)113 public static void testPark(Object blocker) throws Exception { 114 Thread holder = new Thread(() -> { 115 LockSupport.parkNanos(blocker, 10); // Should round up to one millisecond 116 }, "ParkThread"); 117 holder.start(); 118 holder.join(); 119 } 120 testInteruptWait(final Monitors.NamedLock lk)121 public static void testInteruptWait(final Monitors.NamedLock lk) throws Exception { 122 final Monitors.LockController controller1 = new Monitors.LockController(lk); 123 controller1.DoLock(); 124 controller1.waitForLockToBeHeld(); 125 controller1.DoWait(); 126 controller1.waitForNotifySleep(); 127 try { 128 controller1.interruptWorker(); 129 controller1.waitForLockToBeHeld(); 130 controller1.DoUnlock(); 131 System.out.println("No Exception thrown!"); 132 } catch (Monitors.TestException e) { 133 printStackTrace(e); 134 } 135 controller1.DoCleanup(); 136 } 137 testIllegalWait(final Monitors.NamedLock lk)138 public static void testIllegalWait(final Monitors.NamedLock lk) throws Exception { 139 Monitors.LockController controller1 = new Monitors.LockController(lk, /*timed_wait time*/-100); 140 controller1.DoLock(); 141 controller1.waitForLockToBeHeld(); 142 try { 143 controller1.DoTimedWait(); 144 controller1.waitForNotifySleep(); 145 controller1.waitForLockToBeHeld(); 146 controller1.DoUnlock(); 147 System.out.println("No Exception thrown!"); 148 } catch (Monitors.TestException e) { 149 printStackTrace(e); 150 } 151 controller1.DoCleanup(); 152 } 153 testUnlockedWait(final Monitors.NamedLock lk)154 public static void testUnlockedWait(final Monitors.NamedLock lk) throws Exception { 155 synchronized (lk) { 156 Thread thd = new Thread(() -> { 157 try { 158 Method m = Object.class.getDeclaredMethod("wait"); 159 m.invoke(lk); 160 } catch (Exception e) { 161 printStackTrace(e); 162 } 163 }, "Unlocked wait thread:"); 164 thd.start(); 165 thd.join(); 166 } 167 } 168 testLock(Monitors.NamedLock lk)169 public static void testLock(Monitors.NamedLock lk) throws Exception { 170 Monitors.LockController controller1 = new Monitors.LockController(lk); 171 Monitors.LockController controller2 = new Monitors.LockController(lk); 172 controller1.DoLock(); 173 controller1.waitForLockToBeHeld(); 174 controller2.DoLock(); 175 if (controller2.IsLocked()) { 176 throw new Exception("c2 was able to gain lock while it was held by c1"); 177 } 178 controller2.waitForContendedSleep(); 179 controller1.DoUnlock(); 180 controller2.waitForLockToBeHeld(); 181 controller2.DoUnlock(); 182 } 183 testWait(Monitors.NamedLock lk)184 public static void testWait(Monitors.NamedLock lk) throws Exception { 185 Monitors.LockController controller1 = new Monitors.LockController(lk); 186 Monitors.LockController controller2 = new Monitors.LockController(lk); 187 controller1.DoLock(); 188 controller1.waitForLockToBeHeld(); 189 controller1.DoWait(); 190 controller1.waitForNotifySleep(); 191 try (AutoCloseable suppress = SuppressContention(controller2)) { 192 // If controller1 has a spurious wakeup we could see contention here. Suppress it so it won't 193 // cause the test to fail. 194 controller2.DoLock(); 195 controller2.waitForLockToBeHeld(); 196 controller2.DoNotifyAll(); 197 controller2.DoUnlock(); 198 } 199 controller1.waitForLockToBeHeld(); 200 controller1.DoUnlock(); 201 } 202 testTimedWait(Monitors.NamedLock lk)203 public static void testTimedWait(Monitors.NamedLock lk) throws Exception { 204 // Time to wait (1 hour). We will wake it up before timeout. 205 final long millis = 60l * 60l * 1000l; 206 Monitors.LockController controller1 = new Monitors.LockController(lk, millis); 207 Monitors.LockController controller2 = new Monitors.LockController(lk); 208 controller1.DoLock(); 209 controller1.waitForLockToBeHeld(); 210 controller1.DoTimedWait(); 211 controller1.waitForNotifySleep(); 212 try (AutoCloseable suppress = SuppressContention(controller2)) { 213 // If controller1 has a spurious wakeup we could see contention here. Suppress it so it won't 214 // cause the test to fail. 215 controller2.DoLock(); 216 controller2.waitForLockToBeHeld(); 217 controller2.DoNotifyAll(); 218 controller2.DoUnlock(); 219 } 220 controller1.waitForLockToBeHeld(); 221 controller1.DoUnlock(); 222 } 223 testTimedWaitTimeout(Monitors.NamedLock lk)224 public static void testTimedWaitTimeout(Monitors.NamedLock lk) throws Exception { 225 // Time to wait (10 seconds). We will wait for the timeout. 226 final long millis = 10l * 1000l; 227 Monitors.LockController controller1 = new Monitors.LockController(lk, millis); 228 controller1.DoLock(); 229 controller1.waitForLockToBeHeld(); 230 System.out.println("Waiting for 10 seconds."); 231 controller1.DoTimedWait(); 232 controller1.waitForNotifySleep(); 233 controller1.DoUnlock(); 234 System.out.println("Wait finished with timeout."); 235 } 236 } 237