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.util.Arrays;
20 import java.util.List;
21 import java.util.ListIterator;
22 import java.util.function.Consumer;
23 import java.util.function.Function;
24 
25 public class Test1922 {
26   // Set to true to run with all combinations of locks. This isn't really needed for the test to be
27   // useful and fully representative.
28   public final static boolean ALL_COMBOS = false;
29 
30   // A runnable that lets us know when a different thread is paused.
31   public static class ThreadPauser implements Runnable {
32     private boolean suspend;
33     private volatile Thread paused_thread;
ThreadPauser(boolean suspend)34     public ThreadPauser(boolean suspend) {
35       paused_thread = null;
36       this.suspend = suspend;
37     }
ThreadPauser()38     public ThreadPauser() {
39       paused_thread = null;
40       this.suspend = false;
41     }
42 
43     @Override
run()44     public void run() {
45       this.paused_thread = Thread.currentThread();
46       if (suspend) {
47         Suspension.suspend(paused_thread);
48       }
49       while (this.paused_thread != null) {}
50     }
51 
waitForOtherThreadToPause()52     public void waitForOtherThreadToPause() {
53       while (this.paused_thread == null) {}
54       if (suspend) {
55         while (!Suspension.isSuspended(this.paused_thread)) {}
56       }
57     }
58 
wakeupOtherThread()59     public void wakeupOtherThread() {
60       if (this.paused_thread == null) {
61         throw new Error("Other thread is not paused!");
62       }
63       if (suspend) {
64         Suspension.resume(this.paused_thread);
65         while (Suspension.isSuspended(this.paused_thread)) {}
66       }
67       this.paused_thread = null;
68     }
69   }
70 
71   // A class with a number of monitor operations in its methods.
72   public static class Target {
73     public String name;
Target(String name)74     public Target(String name) { this.name = name; }
toString()75     public String toString() { return "Target(\"" + name + "\")"; }
76 
77     // synchronize on Target.class
lockClass(Runnable safepoint)78     public void lockClass(Runnable safepoint) {
79       synchronized (this.getClass()) {
80         safepoint.run();
81       }
82     }
83 
84     // use java synchronized method.
lockSync(Runnable safepoint)85     public synchronized void lockSync(Runnable safepoint) {
86       safepoint.run();
87     }
88 
89     // use java synchronized method and synchronize on another object.
lockExtra(Object l, Runnable safepoint)90     public synchronized void lockExtra(Object l, Runnable safepoint) {
91       synchronized (l) {
92         safepoint.run();
93       }
94     }
95 
96     // monitor enter the object 'l' in native code.
lockNative(Object l, Runnable safepoint)97     public native void lockNative(Object l, Runnable safepoint);
98 
99     // monitor enter 'this' in native code.
lockThisNative(Runnable safepoint)100     public native void lockThisNative(Runnable safepoint);
101 
102     // synchronize on 'l'
lockOther(Object l, Runnable safepoint)103     public void lockOther(Object l, Runnable safepoint) {
104       synchronized (l) {
105         safepoint.run();
106       }
107     }
108 
109     // Don't do anything. Just call the next method.
callSafepoint(Runnable safepoint)110     public void callSafepoint(Runnable safepoint) {
111       safepoint.run();
112     }
113   }
114 
115   // A lock with a toString.
116   public static class NamedLock {
117     public String name;
NamedLock(String name)118     public NamedLock(String name) { this.name = name; }
toString()119     public String toString() { return "NamedLock(\"" + name + "\")"; }
120   }
121 
sortByString(Object[] arr)122   private static Object[] sortByString(Object[] arr) {
123     Arrays.sort(arr, (a, b) -> a.toString().compareTo(b.toString()));
124     return arr;
125   }
126 
127   public static class PrintOwnedMonitorsStackDepthRunnable implements Runnable {
128     public final Thread target;
PrintOwnedMonitorsStackDepthRunnable(Thread target)129     public PrintOwnedMonitorsStackDepthRunnable(Thread target) {
130       this.target = target;
131     }
run()132     public void run() {
133       System.out.println("Owned monitors: " +
134           Arrays.toString(sortByString(getOwnedMonitorStackDepthInfo(target))));
135     }
136   }
137 
138   public static class PrintOwnedMonitorsRunnable implements Runnable {
139     public final Thread target;
PrintOwnedMonitorsRunnable(Thread target)140     public PrintOwnedMonitorsRunnable(Thread target) {
141       this.target = target;
142     }
run()143     public void run() {
144       System.out.println("Owned monitors: " +
145           Arrays.toString(sortByString(getOwnedMonitors(target))));
146     }
147   }
148 
run()149   public static void run() throws Exception {
150     setupTest();
151 
152     System.out.println("owner-monitors, This thread");
153     runTestsCurrentThread("owned-monitor",
154         new PrintOwnedMonitorsRunnable(Thread.currentThread()));
155 
156     System.out.println("owner-monitors, no suspend, Other thread");
157     runTestsOtherThread("owned-monitor", false,
158         (t) -> { new PrintOwnedMonitorsRunnable(t).run(); });
159 
160     System.out.println("owner-monitors, suspend, Other thread");
161     runTestsOtherThread("owned-monitor", true,
162         (t) -> { new PrintOwnedMonitorsRunnable(t).run(); });
163 
164     System.out.println("owner-monitors-stack-depth, This thread");
165     runTestsCurrentThread("owned-stack-depth",
166         new PrintOwnedMonitorsStackDepthRunnable(Thread.currentThread()));
167 
168     System.out.println("owner-monitors-stack-depth, no suspend, other thread");
169     runTestsOtherThread("owned-stack-depth", false,
170         (t) -> { new PrintOwnedMonitorsStackDepthRunnable(t).run(); });
171 
172     System.out.println("owner-monitors-stack-depth, suspend, other thread");
173     runTestsOtherThread("owned-stack-depth", true,
174         (t) -> { new PrintOwnedMonitorsStackDepthRunnable(t).run(); });
175   }
176 
runTestsOtherThread(String name, boolean suspend, Consumer<Thread> printer)177   public static void runTestsOtherThread(String name, boolean suspend, Consumer<Thread> printer) {
178     final Target t = new Target("Other thread test (suspend: " + suspend + "): " + name);
179     final NamedLock l1 = new NamedLock("Lock 1");
180     final NamedLock l2 = new NamedLock("Lock 2");
181     final NamedLock l3 = new NamedLock("Lock 3");
182 
183     List<Function<Runnable, Runnable>> MkSafepoints = Arrays.asList(
184       (r) -> { return new CallLockOther(t, l1, r); },
185       (r) -> { return new CallLockExtra(t, l2, r); },
186       (r) -> { return new CallLockNative(t, l3, r); },
187       (r) -> { return new CallLockThisNative(t, r); },
188       (r) -> { return new CallLockClass(t, r); },
189       (r) -> { return new CallLockSync(t, r); },
190       (r) -> { return new CallSafepoint(t, r); }
191     );
192     // Use ListIterators so we can have elements in the test multiple times.
193     ListIterator<Function<Runnable, Runnable>> li1 = MkSafepoints.listIterator();
194     for (Function<Runnable, Runnable> r1 = li1.next(); li1.hasNext(); r1 = li1.next()) {
195       ListIterator<Function<Runnable, Runnable>> li2 =
196           MkSafepoints.listIterator(ALL_COMBOS ? 0 : li1.previousIndex());
197       for (Function<Runnable, Runnable> r2 = li2.next(); li2.hasNext(); r2 = li2.next()) {
198         ListIterator<Function<Runnable, Runnable>> li3 =
199             MkSafepoints.listIterator(ALL_COMBOS ? 0 : li2.previousIndex());
200         for (Function<Runnable, Runnable> r3 = li3.next(); li3.hasNext(); r3 = li3.next()) {
201           System.out.println("Running: " + Arrays.toString(
202               new Object[] {
203                 r1.apply(null).getClass(),
204                 r2.apply(null).getClass(),
205                 r3.apply(null).getClass(),
206               }));
207           try {
208             final ThreadPauser pause = new ThreadPauser(suspend);
209             final Thread thr = new Thread(r1.apply(r2.apply(r3.apply(pause))));
210             thr.start();
211             pause.waitForOtherThreadToPause();
212             printer.accept(thr);
213             pause.wakeupOtherThread();
214             thr.join();
215           } catch (Exception e) {
216             throw new Error("Exception in test." , e);
217           }
218         }
219       }
220     }
221   }
runTestsCurrentThread(String name, Runnable printer)222   public static void runTestsCurrentThread(String name, Runnable printer) {
223     final Target t = new Target("Current thread test: " + name);
224     final NamedLock l1 = new NamedLock("Lock 1");
225     final NamedLock l2 = new NamedLock("Lock 2");
226     final NamedLock l3 = new NamedLock("Lock 3");
227 
228     List<Function<Runnable, Runnable>> MkSafepoints = Arrays.asList(
229       (r) -> { return new CallLockOther(t, l1, r); },
230       (r) -> { return new CallLockExtra(t, l2, r); },
231       (r) -> { return new CallLockNative(t, l3, r); },
232       (r) -> { return new CallLockThisNative(t, r); },
233       (r) -> { return new CallLockClass(t, r); },
234       (r) -> { return new CallLockSync(t, r); },
235       (r) -> { return new CallSafepoint(t, r); }
236     );
237     ListIterator<Function<Runnable, Runnable>> li1 = MkSafepoints.listIterator();
238     for (Function<Runnable, Runnable> r1 = li1.next(); li1.hasNext(); r1 = li1.next()) {
239       ListIterator<Function<Runnable, Runnable>> li2 =
240           MkSafepoints.listIterator(ALL_COMBOS ? 0 : li1.previousIndex());
241       for (Function<Runnable, Runnable> r2 = li2.next(); li2.hasNext(); r2 = li2.next()) {
242         ListIterator<Function<Runnable, Runnable>> li3 =
243             MkSafepoints.listIterator(ALL_COMBOS ? 0 : li2.previousIndex());
244         for (Function<Runnable, Runnable> r3 = li3.next(); li3.hasNext(); r3 = li3.next()) {
245           System.out.println("Running: " + Arrays.toString(
246               new Object[] {
247                 r1.apply(null).getClass(),
248                 r2.apply(null).getClass(),
249                 r3.apply(null).getClass(),
250               }));
251           r1.apply(r2.apply(r3.apply(printer))).run();
252         }
253       }
254     }
255   }
256 
setupTest()257   public static native void setupTest();
getOwnedMonitors(Thread thr)258   public static native Object[] getOwnedMonitors(Thread thr);
getOwnedMonitorStackDepthInfo(Thread thr)259   public static native MonitorStackDepthInfo[] getOwnedMonitorStackDepthInfo(Thread thr);
260   public static class MonitorStackDepthInfo {
261     public final int depth;
262     public final Object monitor;
MonitorStackDepthInfo(int depth, Object monitor)263     public MonitorStackDepthInfo(int depth, Object monitor) {
264       this.depth = depth;
265       this.monitor = monitor;
266     }
toString()267     public String toString() {
268       return "{ depth: " + depth + ", monitor: \"" + monitor.toString() + "\" }";
269     }
270   }
271 
272   // We want to avoid synthetic methods that would mess up our stack-depths so we make everything
273   // explicit here.
274   public static class CallSafepoint implements Runnable {
275     public final Target target;
276     public final Runnable safepoint;
CallSafepoint(Target target, Runnable safepoint)277     public CallSafepoint(Target target, Runnable safepoint) {
278       this.target = target;
279       this.safepoint = safepoint;
280     }
run()281     public void run() {
282       target.callSafepoint(safepoint);
283     }
284   }
285   public static class CallLockOther implements Runnable {
286     public final Target target;
287     public final Object l;
288     public final Runnable safepoint;
CallLockOther(Target target, Object l, Runnable safepoint)289     public CallLockOther(Target target, Object l, Runnable safepoint) {
290       this.target = target;
291       this.l = l;
292       this.safepoint = safepoint;
293     }
run()294     public void run() {
295       target.lockOther(l, safepoint);
296     }
297   }
298   public static class CallLockExtra implements Runnable {
299     public final Target target;
300     public final Object l;
301     public final Runnable safepoint;
CallLockExtra(Target target, Object l, Runnable safepoint)302     public CallLockExtra(Target target, Object l, Runnable safepoint) {
303       this.target = target;
304       this.l = l;
305       this.safepoint = safepoint;
306     }
run()307     public void run() {
308       target.lockExtra(l, safepoint);
309     }
310   }
311   public static class CallLockThisNative implements Runnable {
312     public final Target target;
313     public final Runnable safepoint;
CallLockThisNative(Target target, Runnable safepoint)314     public CallLockThisNative(Target target, Runnable safepoint) {
315       this.target = target;
316       this.safepoint = safepoint;
317     }
run()318     public void run() {
319       target.lockThisNative(safepoint);
320     }
321   }
322   public static class CallLockNative implements Runnable {
323     public final Target target;
324     public final Object l;
325     public final Runnable safepoint;
CallLockNative(Target target, Object l, Runnable safepoint)326     public CallLockNative(Target target, Object l, Runnable safepoint) {
327       this.target = target;
328       this.l = l;
329       this.safepoint = safepoint;
330     }
run()331     public void run() {
332       target.lockNative(l, safepoint);
333     }
334   }
335   public static class CallLockClass implements Runnable {
336     public final Target target;
337     public final Runnable safepoint;
CallLockClass(Target target, Runnable safepoint)338     public CallLockClass(Target target, Runnable safepoint) {
339       this.target = target;
340       this.safepoint = safepoint;
341     }
run()342     public void run() {
343       target.lockClass(safepoint);
344     }
345   }
346   public static class CallLockSync implements Runnable {
347     public final Target target;
348     public final Runnable safepoint;
CallLockSync(Target target, Runnable safepoint)349     public CallLockSync(Target target, Runnable safepoint) {
350       this.target = target;
351       this.safepoint = safepoint;
352     }
run()353     public void run() {
354       target.lockSync(safepoint);
355     }
356   }
357 }
358