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