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