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 {
printStackTrace(Throwable t)32   public static void printStackTrace(Throwable t) {
33     System.out.println("Caught exception: " + t);
34     for (Throwable c = t.getCause(); c != null; c = c.getCause()) {
35       System.out.println("\tCaused by: " +
36           (Test1931.class.getPackage().equals(c.getClass().getPackage())
37            ? c.toString() : c.getClass().toString()));
38     }
39   }
40 
handleMonitorEnter(Thread thd, Object lock)41   public static void handleMonitorEnter(Thread thd, Object lock) {
42     System.out.println(thd.getName() + " contended-LOCKING " + lock);
43   }
44 
handleMonitorEntered(Thread thd, Object lock)45   public static void handleMonitorEntered(Thread thd, Object lock) {
46     System.out.println(thd.getName() + " LOCKED " + lock);
47   }
handleMonitorWait(Thread thd, Object lock, long timeout)48   public static void handleMonitorWait(Thread thd, Object lock, long timeout) {
49     System.out.println(thd.getName() + " start-monitor-wait " + lock + " timeout: " + timeout);
50   }
51 
handleMonitorWaited(Thread thd, Object lock, boolean timed_out)52   public static void handleMonitorWaited(Thread thd, Object lock, boolean timed_out) {
53     System.out.println(thd.getName() + " monitor-waited " + lock + " timed_out: " + timed_out);
54   }
55 
run()56   public static void run() throws Exception {
57     Monitors.setupMonitorEvents(
58         Test1931.class,
59         Test1931.class.getDeclaredMethod("handleMonitorEnter", Thread.class, Object.class),
60         Test1931.class.getDeclaredMethod("handleMonitorEntered", Thread.class, Object.class),
61         Test1931.class.getDeclaredMethod("handleMonitorWait",
62           Thread.class, Object.class, Long.TYPE),
63         Test1931.class.getDeclaredMethod("handleMonitorWaited",
64           Thread.class, Object.class, Boolean.TYPE),
65         Monitors.NamedLock.class,
66         null);
67 
68     System.out.println("Testing contended locking.");
69     testLock(new Monitors.NamedLock("Lock testLock"));
70 
71     System.out.println("Testing park.");
72     testPark(new Monitors.NamedLock("Parking blocker object"));
73 
74     System.out.println("Testing monitor wait.");
75     testWait(new Monitors.NamedLock("Lock testWait"));
76 
77     System.out.println("Testing monitor timed wait.");
78     testTimedWait(new Monitors.NamedLock("Lock testTimedWait"));
79 
80     System.out.println("Testing monitor timed with timeout.");
81     testTimedWaitTimeout(new Monitors.NamedLock("Lock testTimedWaitTimeout"));
82 
83     // TODO It would be good (but annoying) to do this with jasmin/smali in order to test if it's
84     // different without the reflection.
85     System.out.println("Waiting on an unlocked monitor.");
86     testUnlockedWait(new Monitors.NamedLock("Lock testUnlockedWait"));
87 
88     System.out.println("Waiting with an illegal argument (negative timeout)");
89     testIllegalWait(new Monitors.NamedLock("Lock testIllegalWait"));
90 
91     System.out.println("Interrupt a monitor being waited on.");
92     testInteruptWait(new Monitors.NamedLock("Lock testInteruptWait"));
93   }
94 
testPark(Object blocker)95   public static void testPark(Object blocker) throws Exception {
96     Thread holder = new Thread(() -> {
97       LockSupport.parkNanos(blocker, 10); // Should round up to one millisecond
98     }, "ParkThread");
99     holder.start();
100     holder.join();
101   }
102 
testInteruptWait(final Monitors.NamedLock lk)103   public static void testInteruptWait(final Monitors.NamedLock lk) throws Exception {
104     final Monitors.LockController controller1 = new Monitors.LockController(lk);
105     controller1.DoLock();
106     controller1.waitForLockToBeHeld();
107     controller1.DoWait();
108     controller1.waitForNotifySleep();
109     try {
110       controller1.interruptWorker();
111       controller1.waitForLockToBeHeld();
112       controller1.DoUnlock();
113       System.out.println("No Exception thrown!");
114     } catch (Monitors.TestException e) {
115       printStackTrace(e);
116     }
117     controller1.DoCleanup();
118   }
119 
testIllegalWait(final Monitors.NamedLock lk)120   public static void testIllegalWait(final Monitors.NamedLock lk) throws Exception {
121     Monitors.LockController controller1 = new Monitors.LockController(lk, /*timed_wait time*/-100);
122     controller1.DoLock();
123     controller1.waitForLockToBeHeld();
124     try {
125       controller1.DoTimedWait();
126       controller1.waitForNotifySleep();
127       controller1.waitForLockToBeHeld();
128       controller1.DoUnlock();
129       System.out.println("No Exception thrown!");
130     } catch (Monitors.TestException e) {
131       printStackTrace(e);
132     }
133     controller1.DoCleanup();
134   }
135 
testUnlockedWait(final Monitors.NamedLock lk)136   public static void testUnlockedWait(final Monitors.NamedLock lk) throws Exception {
137     synchronized (lk) {
138       Thread thd = new Thread(() -> {
139         try {
140           Method m = Object.class.getDeclaredMethod("wait");
141           m.invoke(lk);
142         } catch (Exception e) {
143           printStackTrace(e);
144         }
145       }, "Unlocked wait thread:");
146       thd.start();
147       thd.join();
148     }
149   }
150 
testLock(Monitors.NamedLock lk)151   public static void testLock(Monitors.NamedLock lk) throws Exception {
152     Monitors.LockController controller1 = new Monitors.LockController(lk);
153     Monitors.LockController controller2 = new Monitors.LockController(lk);
154     controller1.DoLock();
155     controller1.waitForLockToBeHeld();
156     controller2.DoLock();
157     if (controller2.IsLocked()) {
158       throw new Exception("c2 was able to gain lock while it was held by c1");
159     }
160     controller2.waitForContendedSleep();
161     controller1.DoUnlock();
162     controller2.waitForLockToBeHeld();
163     controller2.DoUnlock();
164   }
165 
testWait(Monitors.NamedLock lk)166   public static void testWait(Monitors.NamedLock lk) throws Exception {
167     Monitors.LockController controller1 = new Monitors.LockController(lk);
168     Monitors.LockController controller2 = new Monitors.LockController(lk);
169     controller1.DoLock();
170     controller1.waitForLockToBeHeld();
171     controller1.DoWait();
172     controller1.waitForNotifySleep();
173     controller2.DoLock();
174     controller2.waitForLockToBeHeld();
175     controller2.DoNotifyAll();
176     controller2.DoUnlock();
177     controller1.waitForLockToBeHeld();
178     controller1.DoUnlock();
179   }
180 
testTimedWait(Monitors.NamedLock lk)181   public static void testTimedWait(Monitors.NamedLock lk) throws Exception {
182     // Time to wait (1 hour). We will wake it up before timeout.
183     final long millis =  60l * 60l * 1000l;
184     Monitors.LockController controller1 = new Monitors.LockController(lk, millis);
185     Monitors.LockController controller2 = new Monitors.LockController(lk);
186     controller1.DoLock();
187     controller1.waitForLockToBeHeld();
188     controller1.DoTimedWait();
189     controller1.waitForNotifySleep();
190     controller2.DoLock();
191     controller2.waitForLockToBeHeld();
192     controller2.DoNotifyAll();
193     controller2.DoUnlock();
194     controller1.waitForLockToBeHeld();
195     controller1.DoUnlock();
196   }
197 
testTimedWaitTimeout(Monitors.NamedLock lk)198   public static void testTimedWaitTimeout(Monitors.NamedLock lk) throws Exception {
199     // Time to wait (10 seconds). We will wait for the timeout.
200     final long millis =  10l * 1000l;
201     Monitors.LockController controller1 = new Monitors.LockController(lk, millis);
202     controller1.DoLock();
203     controller1.waitForLockToBeHeld();
204     System.out.println("Waiting for 10 seconds.");
205     controller1.DoTimedWait();
206     controller1.waitForNotifySleep();
207     controller1.DoUnlock();
208     System.out.println("Wait finished with timeout.");
209   }
210 }
211