1/*
2 * Copyright (c) 2003, 2016, 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 * @test
26 * @bug     4530538
27 * @summary Basic unit test of ThreadInfo.getLockName()
28 *          and ThreadInfo.getLockOwnerName()
29 * @author  Mandy Chung
30 * @author  Jaroslav Bachorik
31 *
32 * @library /lib/testlibrary
33 *
34 * @build jdk.testlibrary.*
35 * @run main/othervm Locks
36 */
37import java.lang.management.*;
38import java.util.Arrays;
39import java.util.Optional;
40import java.util.concurrent.Phaser;
41import java.util.function.Predicate;
42import jdk.testlibrary.LockFreeLogManager;
43
44public class Locks {
45
46    private static final Object OBJA = new Object();
47    private static final Object OBJB = new Object();
48    private static final EnhancedWaiter OBJC = new EnhancedWaiter();
49    private static final ThreadMXBean TM = ManagementFactory.getThreadMXBean();
50    private static final LockFreeLogManager LOGGER = new LockFreeLogManager();
51
52    private static String getLockName(Object lock) {
53        if (lock == null) return null;
54
55        return lock.getClass().getName() + '@' +
56                Integer.toHexString(System.identityHashCode(lock));
57    }
58
59    private static void assertNoLock(Thread t) {
60        if (t == null) {
61            return;
62        }
63        Optional<ThreadInfo> result = Arrays.asList(
64                TM.getThreadInfo(TM.getAllThreadIds(), true, true)).
65                stream().
66                filter(tInfo -> (tInfo != null && tInfo.getLockOwnerName() != null)
67                        ? tInfo.getLockOwnerName().equals(t.getName()) : false).
68                findAny();
69        if (result.isPresent()) {
70            throw new RuntimeException("Thread " + t.getName() + " is not "
71                    + "supposed to be hold any lock. Currently owning lock : "
72                    + result.get().getLockName());
73        }
74    }
75
76   /*
77    * Handy debug function to check if error condition is because of test code or not.
78    */
79    private static void printStackTrace(Thread thread) {
80        if (thread == null) {
81            return;
82        }
83        StackTraceElement[] stackTrace = thread.getStackTrace();
84        log("Stack dump : Thread -> " + thread.getName());
85        for (StackTraceElement stackTraceEl : stackTrace) {
86            log("\t" + stackTraceEl.toString());
87        }
88    }
89
90    private static void assertThreadState(Thread t, Thread.State expectedState) {
91        long tid = t.getId();
92        if (expectedState == Thread.State.BLOCKED
93                && TM.getThreadInfo(tid).getThreadState() != Thread.State.BLOCKED) {
94            int retryCount = 0;
95            printStackTrace(t);
96            while (TM.getThreadInfo(tid).getThreadState() != Thread.State.BLOCKED) {
97                if (retryCount++ > 500) {
98                    printStackTrace(t);
99                    throw new RuntimeException("Thread " + t.getName() + " is at "
100                            + TM.getThreadInfo(tid).getThreadState() + " state but is expected to "
101                            + "be in Thread.State = " + expectedState);
102                }
103                goSleep(100);
104            }
105        }
106        if (!TM.getThreadInfo(tid).getThreadState().equals(expectedState)) {
107            printStackTrace(t);
108            throw new RuntimeException("Thread " + t.getName() + " is at "
109                    + TM.getThreadInfo(tid).getThreadState() + " state but is expected to "
110                    + "be in Thread.State = " + expectedState);
111        }
112    }
113
114   /*
115    * Do slow check if thread is blocked on a lock. It is possible that last thread
116    * to come out of Phaser might still be in Phaser call stack (Unsafe.park) and
117    * hence might eventually acquire expected lock.
118    */
119    private static void checkBlockedObject(Thread t, Object lock, Thread owner) {
120        long tid = t.getId();
121        String result = TM.getThreadInfo(tid).getLockName();
122        final String expectedLock = (lock != null ? getLockName(lock) : null);
123        Predicate<String> p = (res) -> ((res != null && !res.equals(expectedLock))
124                || (res == null && expectedLock != null));
125
126        if (p.test(result)) {
127            printStackTrace(t);
128            int retryCount = 0;
129            while (p.test(result)) {
130                if (retryCount++ > 500) {
131                    printStackTrace(t);
132                    throw new RuntimeException("Thread " + t.getName() + " is blocked on "
133                            + expectedLock + " but got " + result);
134                }
135                goSleep(100);
136                result = TM.getThreadInfo(tid).getLockName();
137            }
138        }
139
140        result = TM.getThreadInfo(tid).getLockOwnerName();
141        final String expectedOwner = (owner != null ? owner.getName() : null);
142
143        p = (res) -> ((res != null && !res.equals(expectedOwner))
144                || (res == null && expectedOwner != null));
145        if (p.test(result)) {
146            printStackTrace(t);
147            throw new RuntimeException("Owner of " + lock + " should be "
148                    + expectedOwner + " but got " + result);
149        }
150    }
151
152    private static void goSleep(long ms){
153        try {
154            Thread.sleep(ms);
155        } catch (InterruptedException ex) {
156            throw new RuntimeException(ex);
157        }
158    }
159
160    private static volatile int dummyCounter = 0;
161
162    static class LockAThread extends Thread {
163        private final Phaser p;
164        public LockAThread(Phaser p) {
165            super("LockAThread");
166            this.p = p;
167        }
168        @Override
169        public void run() {
170            synchronized(OBJA) {
171                // block here while LockBThread holds OBJB
172                log("LockAThread about to block on OBJB");
173                p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
174                synchronized(OBJB) {
175                    dummyCounter++;
176                }
177            }
178            p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
179            log("LockAThread about to exit");
180            // Make sure the current thread is not holding any lock
181            assertNoLock(this);
182        }
183    }
184
185    static class LockBThread extends Thread {
186        private final Phaser p;
187        public LockBThread(Phaser p) {
188            super("LockBThread");
189            this.p = p;
190        }
191        @Override
192        public void run() {
193            synchronized(OBJB) {
194                log("LockBThread about to block on OBJC");
195                p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
196                // Signal main thread about to block on OBJC
197                synchronized(OBJC) {
198                    dummyCounter++;
199                }
200            }
201            p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
202            log("LockBThread about to exit");
203            // Make sure the current thread is not holding any lock
204            assertNoLock(this);
205        }
206    }
207
208   /*
209    * Must be invoked from within a synchronized context
210    */
211    private static class EnhancedWaiter {
212
213        boolean isNotified = false;
214
215        public void doWait() throws InterruptedException {
216            while (!isNotified) {
217                wait();
218            }
219            isNotified = false;
220        }
221
222        public void doNotify() {
223            isNotified = true;
224            notify();
225        }
226    }
227
228    private static WaitingThread waiter;
229    private static final Object ready = new Object();
230    private static CheckerThread checker;
231
232    static class WaitingThread extends Thread {
233        private final Phaser p;
234
235        volatile boolean waiting = false;
236
237        public WaitingThread(Phaser p) {
238            super("WaitingThread");
239            this.p = p;
240        }
241        @Override
242        public void run() {
243            synchronized(OBJC) {
244                log("WaitingThread about to wait on OBJC");
245                try {
246                    // Signal checker thread, about to wait on OBJC.
247                    waiting = false;
248                    p.arriveAndAwaitAdvance(); // Phase 1 (waiting)
249                    waiting = true;
250                    OBJC.doWait();
251                } catch (InterruptedException e) {
252                    throw new RuntimeException(e); // Do not continue test
253                }
254
255                // block until CheckerThread finishes checking
256                log("WaitingThread about to block on ready");
257                // signal checker thread that it is about acquire
258                // object ready.
259                p.arriveAndAwaitAdvance(); // Phase 2 (waiting)
260                synchronized(ready) {
261                    dummyCounter++;
262                }
263            }
264            synchronized(OBJC) {
265                try {
266                    // signal checker thread, about to wait on OBJC
267                    waiting = false;
268                    p.arriveAndAwaitAdvance(); // Phase 3 (waiting)
269                    waiting = true;
270                    OBJC.doWait();
271                } catch (InterruptedException e) {
272                    throw new RuntimeException(e);
273                }
274            }
275            log("WaitingThread about to exit waiting on OBJC 2");
276        }
277
278        public void waitForWaiting() {
279            p.arriveAndAwaitAdvance();
280            while (!waiting) {
281                goSleep(10);
282            }
283            waitForState(State.WAITING);
284        }
285
286        public void waitForBlocked() {
287            p.arriveAndAwaitAdvance();
288            waitForState(State.BLOCKED);
289        }
290
291        private void waitForState(Thread.State state) {
292            while (!waiter.isInterrupted() && waiter.getState() != state) {
293                Thread.yield();
294            }
295        }
296    }
297    static class CheckerThread extends Thread {
298        public CheckerThread() {
299            super("CheckerThread");
300        }
301
302        @Override
303        public void run() {
304            synchronized(ready) {
305                // wait until WaitingThread about to wait for OBJC
306                waiter.waitForWaiting(); // Phase 1 (waiting)
307                assertThreadState(waiter, Thread.State.WAITING);
308                checkBlockedObject(waiter, OBJC, null);
309
310                synchronized(OBJC) {
311                    OBJC.doNotify();
312                }
313                // wait for waiter thread to about to enter
314                // synchronized object ready.
315                waiter.waitForBlocked(); // Phase 2 (waiting)
316                assertThreadState(waiter, Thread.State.BLOCKED);
317                checkBlockedObject(waiter, ready, this);
318            }
319
320            // wait for signal from waiting thread that it is about
321            // wait for OBJC.
322            waiter.waitForWaiting(); // Phase 3 (waiting)
323            synchronized(OBJC) {
324                assertThreadState(waiter, Thread.State.WAITING);
325                checkBlockedObject(waiter, OBJC, Thread.currentThread());
326                OBJC.doNotify();
327            }
328        }
329    }
330
331    public static void main(String args[]) throws Exception {
332        try {
333            Thread mainThread = Thread.currentThread();
334
335            // Test uncontested case
336            LockAThread t1;
337            LockBThread t2;
338
339            Phaser p = new Phaser(3);
340            synchronized(OBJC) {
341                // Make sure the main thread is not holding any lock
342                assertNoLock(mainThread);
343
344                // Test deadlock case
345                // t1 holds lockA and attempts to lock B
346                // t2 holds lockB and attempts to lock C
347                t1 = new LockAThread(p);
348                t1.start();
349
350                t2 = new LockBThread(p);
351                t2.start();
352
353                p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
354                assertThreadState(t2, Thread.State.BLOCKED);
355                checkBlockedObject(t2, OBJC, mainThread);
356                assertThreadState(t1, Thread.State.BLOCKED);
357                checkBlockedObject(t1, OBJB, t2);
358
359                long[] expectedThreads = new long[3];
360                expectedThreads[0] = t1.getId(); // blocked on lockB
361                expectedThreads[1] = t2.getId(); // owner of lockB blocking on lockC
362                expectedThreads[2] = mainThread.getId(); // owner of lockC
363                findThreadsBlockedOn(OBJB, expectedThreads);
364            }
365            p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
366
367            p = new Phaser(2);
368            // Test Object.wait() case
369            waiter = new WaitingThread(p);
370            waiter.start();
371
372            checker = new CheckerThread();
373            checker.start();
374
375            try {
376                waiter.join();
377                checker.join();
378            } catch (InterruptedException e) {
379                throw new RuntimeException(e);
380            }
381        } finally { // log all the messages to STDOUT
382            System.out.println(LOGGER.toString());
383        }
384        System.out.println("Test passed.");
385    }
386
387    private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock)
388            throws Exception {
389        ThreadInfo ownerInfo = null;
390        for (ThreadInfo info : infos) {
391            String blockedLock = info.getLockName();
392            if (lock.equals(blockedLock)) {
393                long threadId = info.getLockOwnerId();
394                if (threadId == -1) {
395                    throw new RuntimeException("TEST FAILED: " +
396                            lock + " expected to have owner");
397                }
398                for (ThreadInfo info1 : infos) {
399                    if (info1.getThreadId() == threadId) {
400                        ownerInfo = info1;
401                        break;
402                    }
403                }
404            }
405        }
406        return ownerInfo;
407    }
408    private static void findThreadsBlockedOn(Object o, long[] expectedThreads)
409            throws Exception {
410        String lock = getLockName(o);
411        // Check with ThreadInfo with no stack trace (i.e. no safepoint)
412        ThreadInfo[] infos = TM.getThreadInfo(TM.getAllThreadIds());
413        doCheck(infos, lock, expectedThreads);
414
415        // Check with ThreadInfo with stack trace
416        infos = TM.getThreadInfo(TM.getAllThreadIds(), 1);
417        doCheck(infos, lock, expectedThreads);
418    }
419
420    private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThreads)
421            throws Exception {
422        ThreadInfo ownerInfo = null;
423        // Find the thread who is blocking on lock
424        for (ThreadInfo info : infos) {
425            String blockedLock = info.getLockName();
426            if (lock.equals(blockedLock)) {
427                log("%s blocked on %s", info.getThreadName(), blockedLock);
428                ownerInfo = info;
429            }
430        }
431        if (ownerInfo == null) {
432            throw new RuntimeException("TEST FAILED: " +
433                    "Can't retrieve ThreadInfo for the blocked thread");
434        }
435
436        long[] threads = new long[10];
437        int count = 0;
438        threads[count++] = ownerInfo.getThreadId();
439        while (ownerInfo.getThreadState() == Thread.State.BLOCKED) {
440            ownerInfo = findOwnerInfo(infos, lock);
441            threads[count++] = ownerInfo.getThreadId();
442            log(" Owner = %s  id = %d",
443                    ownerInfo.getThreadName(),
444                    ownerInfo.getThreadId()
445            );
446            lock = ownerInfo.getLockName();
447            log("%s Id = %d  blocked on %s",
448                    ownerInfo.getThreadName(),
449                    ownerInfo.getThreadId(),
450                    lock
451            );
452        }
453        log("");
454
455        if (count != expectedThreads.length) {
456            throw new RuntimeException("TEST FAILED: " +
457                    "Expected chain of threads not matched; current count =" + count);
458        }
459        for (int i = 0; i < count; i++) {
460            if (threads[i] != expectedThreads[i]) {
461                log("TEST FAILED: Unexpected thread in the chain %s expected to be %s",
462                    threads[i],
463                    expectedThreads[i]
464                );
465            }
466        }
467    }
468
469    private static void log(String format, Object ... args) {
470        LOGGER.log(format + "%n", args);
471    }
472}
473