1 /* 2 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package nsk.monitoring.share.thread; 25 26 import java.lang.management.*; 27 import nsk.share.log.*; 28 import java.util.Map; 29 import java.util.HashMap; 30 import java.util.concurrent.locks.ReentrantLock; 31 import java.util.concurrent.locks.Condition; 32 import java.util.concurrent.locks.Lock; 33 34 /** 35 * Scenario that starts two threads that use locks * to synchronize. 36 * The code is based on tests/java/lang/management/ThreadMXBean/LockingThread.java 37 */ 38 public class SynchronizerLockingThreads implements ThreadMonitoringScenario, LogAware { 39 private static final String[] expectedMethodsThread1 = { 40 "nsk.monitoring.share.thread.SynchronizerLockingThreads$Thread1.runInside", 41 "nsk.monitoring.share.thread.SynchronizerLockingThreads$Thread1.A", 42 "nsk.monitoring.share.thread.SynchronizerLockingThreads$Thread1.B", 43 "nsk.monitoring.share.thread.SynchronizerLockingThreads$Thread1.C", 44 "nsk.monitoring.share.thread.SynchronizerLockingThreads$Thread1.D", 45 "java.lang.Object.wait" 46 }; 47 private static final String[] expectedMethodsThread2 = { 48 "nsk.monitoring.share.thread.SynchronizerLockingThreads$Thread2.runInside" 49 }; 50 private ReentrantLock lock1 = new ReentrantLock(); 51 private ReentrantLock lock2 = new ReentrantLock(); 52 private ReentrantLock lock3 = new ReentrantLock(); 53 private ReentrantLock lock4 = new ReentrantLock(); 54 private CustomLock lock5 = new CustomLock("lock5"); 55 private CustomLock lock6 = new CustomLock("lock6"); 56 private CustomLock lock7 = new CustomLock("lock7"); 57 private ReentrantLock lock8 = new ReentrantLock(); 58 private MonitoringThread thread1; 59 private MonitoringThread thread2; 60 private Log log; 61 private RunType recursionType; 62 private int maxDepth; 63 SynchronizerLockingThreads(Log log, RunType recursionType, int maxDepth)64 public SynchronizerLockingThreads(Log log, RunType recursionType, int maxDepth) { 65 setLog(log); 66 this.recursionType = recursionType; 67 this.maxDepth = maxDepth; 68 thread1 = new Thread1(log, recursionType, maxDepth); 69 thread2 = new Thread2(log, recursionType, maxDepth); 70 } 71 72 static class CustomLock { 73 private String name; 74 CustomLock(String name)75 public CustomLock(String name) { 76 this.name = name; 77 } 78 toString()79 public String toString() { 80 return name; 81 } 82 } 83 84 private class Thread1 extends RecursiveMonitoringThread { 85 private volatile boolean ready = false; 86 private Object readyLock = new Object(); 87 private Map<String, Object[]> lockedMonitors = new HashMap<String, Object[]>(); 88 private Map<String, Lock[]> lockedSynchronizers = new HashMap<String, Lock[]>(); 89 Thread1(Log log, RunType recursionType, int maxDepth)90 public Thread1(Log log, RunType recursionType, int maxDepth) { 91 super(log, recursionType, maxDepth); 92 lockedMonitors.put("D", new Object[] {}); 93 lockedMonitors.put("C", new Object[] { lock6 }); 94 lockedMonitors.put("B", new Object[] { lock5 }); 95 lockedMonitors.put("A", new Object[] { }); 96 lockedSynchronizers.put("D", new ReentrantLock[0]); // no sync locked 97 lockedSynchronizers.put("C", new ReentrantLock[0]); // no sync locked 98 lockedSynchronizers.put("B", new Lock[] {lock4}); 99 lockedSynchronizers.put("A", new Lock[] {lock3, lock2, lock1}); 100 } 101 checkThreadInfo(ThreadInfo info)102 public void checkThreadInfo(ThreadInfo info) { 103 super.checkThreadInfo(info); 104 checkLockInfo(info.getLockInfo(), lock7); 105 checkMonitorInfo(info.getLockedMonitors(), lockedMonitors); 106 checkSynchronizers(info.getLockedSynchronizers(), lockedSynchronizers); 107 } 108 runInside()109 protected void runInside() { 110 A(); 111 } 112 A()113 void A() { 114 lock1.lock(); 115 try { 116 lock2.lock(); 117 try { 118 lock3.lock(); 119 try { 120 B(); 121 } finally { 122 lock3.unlock(); 123 } 124 } finally { 125 lock2.unlock(); 126 } 127 } finally { 128 lock1.unlock(); 129 } 130 } 131 B()132 void B() { 133 lock4.lock(); 134 try { 135 synchronized(lock5) { 136 C(); 137 } 138 } finally { 139 lock4.unlock(); 140 } 141 } 142 C()143 void C() { 144 synchronized(lock6) { 145 D(); 146 } 147 } 148 D()149 void D() { 150 synchronized(lock7) { 151 try { 152 synchronized (readyLock) { 153 ready = true; 154 readyLock.notifyAll(); 155 } 156 lock7.wait(); 157 } catch (InterruptedException e) { 158 throw new RuntimeException(e); 159 } 160 } 161 } 162 163 waitState()164 public void waitState() { 165 synchronized (readyLock) { 166 while (!ready) { 167 try { 168 readyLock.wait(); 169 } catch (InterruptedException e) { 170 log.warn(e); 171 } 172 } 173 } 174 waitThreadState(Thread.State.WAITING); 175 } 176 finish()177 public void finish() { 178 synchronized (lock7) { 179 lock7.notifyAll(); 180 } 181 } 182 isStackTraceElementExpected(StackTraceElement element)183 protected boolean isStackTraceElementExpected(StackTraceElement element) { 184 return super.isStackTraceElementExpected(element) || checkStackTraceElement(element, expectedMethodsThread1); 185 } 186 } 187 188 private class Thread2 extends RecursiveMonitoringThread { 189 private boolean ready = false; 190 private Object readyLock = new Object(); 191 private Map<String, Object[]> lockedMonitors = new HashMap<String, Object[]>(); 192 private Map<String, Lock[]> lockedSynchronizers = new HashMap<String, Lock[]>(); 193 private Condition c = lock8.newCondition(); 194 Thread2(Log log, RunType recursionType, int maxDepth)195 public Thread2(Log log, RunType recursionType, int maxDepth) { 196 super(log, recursionType, maxDepth); 197 } 198 checkThreadInfo(ThreadInfo info)199 public void checkThreadInfo(ThreadInfo info) { 200 super.checkThreadInfo(info); 201 checkLockInfo(info.getLockInfo(), c); 202 checkMonitorInfo(info.getLockedMonitors(), lockedMonitors); 203 checkSynchronizers(info.getLockedSynchronizers(), lockedSynchronizers); 204 } 205 runInside()206 protected void runInside() { 207 lock8.lock(); 208 try { 209 synchronized (readyLock) { 210 ready = true; 211 readyLock.notifyAll(); 212 } 213 c.await(); 214 } catch (InterruptedException e) { 215 throw new RuntimeException(e); 216 } finally { 217 lock8.unlock(); 218 } 219 } 220 waitState()221 public void waitState() { 222 synchronized (readyLock) { 223 while (!ready) { 224 try { 225 readyLock.wait(); 226 } catch (InterruptedException e) { 227 log.warn(e); 228 } 229 } 230 } 231 waitThreadState(Thread.State.WAITING); 232 } 233 finish()234 public void finish() { 235 lock8.lock(); 236 try { 237 c.signalAll(); 238 } finally { 239 lock8.unlock(); 240 } 241 } 242 isStackTraceElementExpected(StackTraceElement element)243 protected boolean isStackTraceElementExpected(StackTraceElement element) { 244 return super.isStackTraceElementExpected(element) || 245 checkStackTraceElement(element, expectedMethodsThread2) || 246 element.getClassName().startsWith("java.util.concurrent.") || 247 element.getClassName().startsWith("jdk.internal.misc."); 248 } 249 } 250 251 begin()252 public void begin() { 253 thread1.begin(); 254 thread2.begin(); 255 } 256 waitState()257 public void waitState() { 258 thread1.waitState(); 259 thread2.waitState(); 260 } 261 check(ThreadMXBean threadMXBean)262 public void check(ThreadMXBean threadMXBean) { 263 long[] ids = new long[] { thread1.getId(), thread2.getId() }; 264 ThreadInfo[] info = threadMXBean.getThreadInfo(ids, true, true); 265 thread1.checkThreadInfo(info[0]); 266 thread2.checkThreadInfo(info[1]); 267 } 268 finish()269 public void finish() { 270 thread1.finish(); 271 thread2.finish(); 272 } 273 end()274 public void end() { 275 thread1.end(); 276 thread2.end(); 277 } 278 setLog(Log log)279 public void setLog(Log log) { 280 this.log = log; 281 } 282 } 283