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