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 
 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 
 75                 public CustomLock(String name) {
 76                         this.name = name;
 77                 }
 78 
 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 
 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 
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 
109                 protected void runInside() {
110                         A();
111                 }
112 
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 
132                 void B() {
133                         lock4.lock();
134                         try {
135                                 synchronized(lock5) {
136                                         C();
137                                 }
138                         } finally {
139                                 lock4.unlock();
140                         }
141                 }
142 
143                 void C() {
144                         synchronized(lock6) {
145                                 D();
146                         }
147                 }
148 
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 
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 
177                 public void finish() {
178                         synchronized (lock7) {
179                                 lock7.notifyAll();
180                         }
181                 }
182 
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 
195                 public Thread2(Log log, RunType recursionType, int maxDepth) {
196                         super(log, recursionType, maxDepth);
197                 }
198 
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 
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 
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 
234                 public void finish() {
235                         lock8.lock();
236                         try {
237                                 c.signalAll();
238                         } finally {
239                                 lock8.unlock();
240                         }
241                 }
242 
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 
252         public void begin() {
253                 thread1.begin();
254                 thread2.begin();
255         }
256 
257         public void waitState() {
258                 thread1.waitState();
259                 thread2.waitState();
260         }
261 
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 
269         public void finish() {
270                 thread1.finish();
271                 thread2.finish();
272         }
273 
274         public void end() {
275                 thread1.end();
276                 thread2.end();
277         }
278 
279         public void setLog(Log log) {
280                 this.log = log;
281         }
282 }