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
17package art;
18
19import java.util.Arrays;
20import java.util.List;
21import java.util.ListIterator;
22import java.util.function.Consumer;
23import java.util.function.Function;
24
25public 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;
34    public ThreadPauser(boolean suspend) {
35      paused_thread = null;
36      this.suspend = suspend;
37    }
38    public ThreadPauser() {
39      paused_thread = null;
40      this.suspend = false;
41    }
42
43    @Override
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
52    public void waitForOtherThreadToPause() {
53      while (this.paused_thread == null) {}
54      if (suspend) {
55        while (!Suspension.isSuspended(this.paused_thread)) {}
56      }
57    }
58
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;
74    public Target(String name) { this.name = name; }
75    public String toString() { return "Target(\"" + name + "\")"; }
76
77    // synchronize on Target.class
78    public void lockClass(Runnable safepoint) {
79      synchronized (this.getClass()) {
80        safepoint.run();
81      }
82    }
83
84    // use java synchronized method.
85    public synchronized void lockSync(Runnable safepoint) {
86      safepoint.run();
87    }
88
89    // use java synchronized method and synchronize on another object.
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.
97    public native void lockNative(Object l, Runnable safepoint);
98
99    // monitor enter 'this' in native code.
100    public native void lockThisNative(Runnable safepoint);
101
102    // synchronize on 'l'
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.
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;
118    public NamedLock(String name) { this.name = name; }
119    public String toString() { return "NamedLock(\"" + name + "\")"; }
120  }
121
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;
129    public PrintOwnedMonitorsStackDepthRunnable(Thread target) {
130      this.target = target;
131    }
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;
140    public PrintOwnedMonitorsRunnable(Thread target) {
141      this.target = target;
142    }
143    public void run() {
144      System.out.println("Owned monitors: " +
145          Arrays.toString(sortByString(getOwnedMonitors(target))));
146    }
147  }
148
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
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  }
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
257  public static native void setupTest();
258  public static native Object[] getOwnedMonitors(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;
263    public MonitorStackDepthInfo(int depth, Object monitor) {
264      this.depth = depth;
265      this.monitor = monitor;
266    }
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;
277    public CallSafepoint(Target target, Runnable safepoint) {
278      this.target = target;
279      this.safepoint = safepoint;
280    }
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;
289    public CallLockOther(Target target, Object l, Runnable safepoint) {
290      this.target = target;
291      this.l = l;
292      this.safepoint = safepoint;
293    }
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;
302    public CallLockExtra(Target target, Object l, Runnable safepoint) {
303      this.target = target;
304      this.l = l;
305      this.safepoint = safepoint;
306    }
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;
314    public CallLockThisNative(Target target, Runnable safepoint) {
315      this.target = target;
316      this.safepoint = safepoint;
317    }
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;
326    public CallLockNative(Target target, Object l, Runnable safepoint) {
327      this.target = target;
328      this.l = l;
329      this.safepoint = safepoint;
330    }
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;
338    public CallLockClass(Target target, Runnable safepoint) {
339      this.target = target;
340      this.safepoint = safepoint;
341    }
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;
349    public CallLockSync(Target target, Runnable safepoint) {
350      this.target = target;
351      this.safepoint = safepoint;
352    }
353    public void run() {
354      target.lockSync(safepoint);
355    }
356  }
357}
358