1 /*
2  * Copyright (c) 2005, 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 /*
25  * @bug     5086470 6358247
26  * @summary SynchronizersLockingThread is used by LockedSynchronizers.
27  *          It will create threads that acquire ReentrantLock and also object
28  *          monitors.
29  * @author  Mandy Chung
30  *
31  * @build ThreadDump Utils
32  */
33 
34 import java.lang.management.*;
35 import java.util.*;
36 import java.util.concurrent.locks.ReentrantLock;
37 import java.util.concurrent.locks.Condition;
38 
39 public class SynchronizerLockingThread extends Thread {
40     static ReentrantLock lock1 = new ReentrantLock();
41     static ReentrantLock lock2 = new ReentrantLock();
42     static ReentrantLock lock3 = new ReentrantLock();
43     static ReentrantLock lock4 = new ReentrantLock();
44     static Lock lock5 = new Lock("lock5");
45     static Lock lock6 = new Lock("lock6");
46     static Lock lock7 = new Lock("lock7");
47     static ReentrantLock lock8 = new ReentrantLock();
48 
49     static SynchronizerLockingThread t1 = new Thread1();
50     static SynchronizerLockingThread t2 = new Thread2();
51     static int count = 2;
startLockingThreads()52     static void startLockingThreads() {
53         t1.setDaemon(true);
54         t2.setDaemon(true);
55         t1.start();
56         t2.start();
57 
58         // wait until t1 and t2 waits
59         while (count != 0) {
60            try {
61                Thread.sleep(100);
62            } catch (InterruptedException e) {
63                throw new RuntimeException(e);
64            }
65         }
66 
67         Utils.waitForBlockWaitingState(t1);
68         Utils.waitForBlockWaitingState(t2);
69     }
70 
getThreadIds()71     static long[] getThreadIds() {
72         return new long[] {t1.getId(), t2.getId()};
73     }
74 
checkLocks(ThreadInfo[] tinfos)75     static void checkLocks(ThreadInfo[] tinfos) throws Exception {
76         int matches = 0;
77         for (ThreadInfo info : tinfos) {
78             if (info.getThreadId() == t1.getId()) {
79                 t1.checkLocks(info);
80                 matches++;
81             }
82             if (info.getThreadId() == t2.getId()) {
83                 t2.checkLocks(info);
84                 matches++;
85             }
86         }
87         if (matches != 2) {
88             throw new RuntimeException("MonitorInfo missing");
89         }
90     }
91 
92     static class Lock {
93         String name;
Lock(String name)94         Lock(String name) {
95             this.name = name;
96         }
toString()97         public String toString() {
98             return name;
99         }
100     }
101 
102     final String threadName;
103     Lock   waitingLock;
104     int    numOwnedMonitors;
105     Map<String, Lock[]> ownedMonitors;
106     Condition       waitingSync;
107     int             numOwnedSyncs;
108     Map<String, ReentrantLock[]> ownedSyncs;
SynchronizerLockingThread(String name)109     public SynchronizerLockingThread(String name) {
110         this.threadName = name;
111     }
112 
setExpectedResult(Lock waitingLock, int numOwnedMonitors, Map<String, Lock[]> ownedMonitors, Condition waitingSync, int numOwnedSyncs, Map<String, ReentrantLock[]> ownedSyncs)113     protected void setExpectedResult(Lock waitingLock,
114                                      int numOwnedMonitors,
115                                      Map<String, Lock[]> ownedMonitors,
116                                      Condition waitingSync,
117                                      int numOwnedSyncs,
118                                      Map<String, ReentrantLock[]> ownedSyncs) {
119         this.waitingLock = waitingLock;
120         this.numOwnedMonitors = numOwnedMonitors;
121         this.ownedMonitors = ownedMonitors;
122         this.waitingSync = waitingSync;
123         this.numOwnedSyncs = numOwnedSyncs;
124         this.ownedSyncs = ownedSyncs;
125     }
126 
checkLocks(ThreadInfo info)127     void checkLocks(ThreadInfo info) throws Exception {
128         checkThreadInfo(info);
129         MonitorInfo[] monitors = info.getLockedMonitors();
130         if (monitors.length != numOwnedMonitors) {
131             ThreadDump.threadDump();
132             throw new RuntimeException("Number of locked monitors = " +
133                 monitors.length +
134                 " not matched. Expected: " + numOwnedMonitors);
135         }
136         // check if each monitor returned in the list is the expected
137         // one
138         for (MonitorInfo m : monitors) {
139             StackTraceElement ste = m.getLockedStackFrame();
140             int depth = m.getLockedStackDepth();
141             checkStackFrame(info, ste, depth);
142             checkMonitor(m, ste.getMethodName());
143         }
144         // check if each expected monitor is included in the returned
145         // list
146         for (Map.Entry<String, Lock[]> e : ownedMonitors.entrySet()) {
147             for (Lock l : e.getValue()) {
148                 checkMonitor(e.getKey(), l, monitors);
149             }
150         }
151 
152         // We can only check if the length matches since we have no
153         // way to get the AbstractOwnableSynchronizer in ReentrantLock
154         LockInfo[] syncs = info.getLockedSynchronizers();
155         if (syncs.length != numOwnedSyncs) {
156             ThreadDump.threadDump();
157             throw new RuntimeException("Number of locked syncs = " +
158                 syncs.length +
159                 " not matched. Expected: " + numOwnedSyncs);
160         }
161     }
162 
checkThreadInfo(ThreadInfo info)163     void checkThreadInfo(ThreadInfo info) throws Exception {
164         if (!getName().equals(info.getThreadName())) {
165             throw new RuntimeException("Name: " + info.getThreadName() +
166                 " not matched. Expected: " + getName());
167         }
168         LockInfo l = info.getLockInfo();
169         if ((waitingLock != null || waitingSync != null) && l == null) {
170             throw new RuntimeException("LockInfo: " + l +
171                 " not matched. Expected: non-null");
172         }
173         if (waitingLock == null && waitingSync == null && l != null) {
174             throw new RuntimeException("LockInfo: " + l +
175                 " not matched. Expected: null");
176         }
177 
178         String waitingLockName;
179         int hcode;
180         if (waitingLock != null) {
181             waitingLockName = waitingLock.getClass().getName();
182             hcode = System.identityHashCode(waitingLock);
183         } else {
184             waitingLockName = waitingSync.getClass().getName();
185             hcode = System.identityHashCode(waitingSync);
186         }
187         if (!waitingLockName.equals(l.getClassName())) {
188             throw new RuntimeException("LockInfo : " + l +
189                 " class name not matched. Expected: " + waitingLockName);
190         }
191         if (hcode != l.getIdentityHashCode()) {
192             throw new RuntimeException("LockInfo: " + l +
193                 " IdentityHashCode not matched. Expected: " + hcode);
194         }
195 
196         String lockName = info.getLockName();
197         String[] s = lockName.split("@");
198         if (!waitingLockName.equals(s[0])) {
199             throw new RuntimeException("LockName: " + lockName +
200                 " class name not matched. Expected: " + waitingLockName);
201         }
202         int i = Integer.parseInt(s[1], 16);
203         if (hcode != i) {
204             throw new RuntimeException("LockName: " + lockName +
205                 " IdentityHashCode not matched. Expected: " + hcode);
206         }
207     }
208 
checkStackFrame(ThreadInfo info, StackTraceElement ste, int depth)209     void checkStackFrame(ThreadInfo info, StackTraceElement ste, int depth) {
210         StackTraceElement[] stacktrace = info.getStackTrace();
211         if (!ste.equals(stacktrace[depth])) {
212             System.out.println("LockedStackFrame:- " + ste);
213             System.out.println("StackTrace at " + depth + " :-" +
214                 stacktrace[depth]);
215             throw new RuntimeException("LockedStackFrame does not match " +
216                 "stack frame in ThreadInfo.getStackTrace");
217         }
218     }
checkMonitor(MonitorInfo m, String methodName)219     void checkMonitor(MonitorInfo m, String methodName) {
220         for (Map.Entry<String, Lock[]> e : ownedMonitors.entrySet()) {
221             if (methodName.equals(e.getKey())) {
222                 for (Lock l : e.getValue()) {
223                     String className = l.getClass().getName();
224                     int hcode = System.identityHashCode(l);
225                     if (className.equals(m.getClassName()) &&
226                         hcode == m.getIdentityHashCode()) {
227                         // monitor matched the expected
228                         return;
229                     }
230                 }
231             }
232         }
233         throw new RuntimeException("Monitor not expected" + m);
234     }
checkMonitor(String methodName, Lock l, MonitorInfo[] monitors)235     void checkMonitor(String methodName, Lock l, MonitorInfo[] monitors) {
236         String className = l.getClass().getName();
237         int hcode = System.identityHashCode(l);
238         for (MonitorInfo m : monitors) {
239             if (className.equals(m.getClassName()) &&
240                 hcode == m.getIdentityHashCode() &&
241                 methodName.equals(m.getLockedStackFrame().getMethodName())) {
242                 return;
243             }
244         }
245         throw new RuntimeException("Monitor not found in the returned list" +
246             " Method: " + methodName + " Lock: " + l);
247 
248     }
249 
250     static class Thread1 extends SynchronizerLockingThread {
Thread1()251         public Thread1() {
252             super("t1");
253             initExpectedResult();
254         }
run()255         public void run() {
256             A();
257         }
A()258         void A() {
259             lock1.lock();
260             try {
261                 lock2.lock();
262                 try {
263                     lock3.lock();
264                     try {
265                         B();
266                     } finally {
267                         lock3.unlock();
268                     }
269                 } finally {
270                     lock2.unlock();
271                 }
272             } finally {
273                 lock1.unlock();
274             }
275         }
B()276         void B() {
277             lock4.lock();
278             try {
279                 synchronized(lock5) {
280                     C();
281                 }
282             } finally {
283                 lock4.unlock();
284             }
285         }
C()286         void C() {
287             synchronized(lock6) {
288                 D();
289             }
290         }
D()291         void D() {
292             synchronized(lock7) {
293                 try {
294                     // signal to about to wait
295                     count--;
296                     lock7.wait();
297                 } catch (InterruptedException e) {
298                     throw new RuntimeException(e);
299                 }
300             }
301         }
302 
303         Map<String, Lock[]> LOCKED_MONITORS;
304         Map<String, ReentrantLock[]> LOCKED_SYNCS;
305         Lock WAITING_LOCK = lock7;
306         int OWNED_MONITORS = 2;
307         int OWNED_SYNCS = 4;
initExpectedResult()308         void initExpectedResult() {
309             LOCKED_MONITORS = new HashMap<String, Lock[]>();
310             LOCKED_MONITORS.put("D", new Lock[0]); // no monitored locked
311             LOCKED_MONITORS.put("C", new Lock[] {lock6});
312             LOCKED_MONITORS.put("B", new Lock[] {lock5});
313             LOCKED_MONITORS.put("A", new Lock[0]);
314 
315             LOCKED_SYNCS = new HashMap<String, ReentrantLock[]>();
316             LOCKED_SYNCS.put("D", new ReentrantLock[0]); // no sync locked
317             LOCKED_SYNCS.put("C", new ReentrantLock[0]); // no sync locked
318             LOCKED_SYNCS.put("B", new ReentrantLock[] {lock4});
319             LOCKED_SYNCS.put("A", new ReentrantLock[] {lock3, lock2, lock1});
320             this.setExpectedResult(WAITING_LOCK,
321                                    OWNED_MONITORS, LOCKED_MONITORS,
322                                    null,
323                                    OWNED_SYNCS, LOCKED_SYNCS);
324         }
325 
326     }
327 
328     static class Thread2 extends SynchronizerLockingThread {
329         Map<String, Lock[]> LOCKED_MONITORS = new HashMap<String, Lock[]>();
330         Map<String, ReentrantLock[]> LOCKED_SYNCS = new HashMap<String, ReentrantLock[]>();
331         Condition c = lock8.newCondition();
332         Condition WAITING_LOCK = c;
333         int OWNED_MONITORS = 0;
334         int OWNED_SYNCS = 0;
Thread2()335         public Thread2() {
336             super("t2");
337             this.setExpectedResult(null,
338                                    OWNED_MONITORS, LOCKED_MONITORS,
339                                    WAITING_LOCK,
340                                    OWNED_SYNCS, LOCKED_SYNCS);
341         }
run()342         public void run() {
343             lock8.lock();
344             try {
345                 synchronized(lock7) {
346                     count--;
347                 }
348                 c.await();
349             } catch (InterruptedException e) {
350                 throw new RuntimeException(e);
351             } finally {
352                 lock8.unlock();
353             }
354             throw new RuntimeException("should not reach here");
355         }
356     }
357 
358 }
359