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
34import java.lang.management.*;
35import java.util.*;
36import java.util.concurrent.locks.ReentrantLock;
37import java.util.concurrent.locks.Condition;
38
39public 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;
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
71    static long[] getThreadIds() {
72        return new long[] {t1.getId(), t2.getId()};
73    }
74
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;
94        Lock(String name) {
95            this.name = name;
96        }
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;
109    public SynchronizerLockingThread(String name) {
110        this.threadName = name;
111    }
112
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
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
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
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    }
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    }
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 {
251        public Thread1() {
252            super("t1");
253            initExpectedResult();
254        }
255        public void run() {
256            A();
257        }
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        }
276        void B() {
277            lock4.lock();
278            try {
279                synchronized(lock5) {
280                    C();
281                }
282            } finally {
283                lock4.unlock();
284            }
285        }
286        void C() {
287            synchronized(lock6) {
288                D();
289            }
290        }
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;
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;
335        public Thread2() {
336            super("t2");
337            this.setExpectedResult(null,
338                                   OWNED_MONITORS, LOCKED_MONITORS,
339                                   WAITING_LOCK,
340                                   OWNED_SYNCS, LOCKED_SYNCS);
341        }
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