Path: blob/master/test/jdk/java/lang/management/ThreadMXBean/Locks.java
41152 views
/*1* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 453053826* @summary Basic unit test of ThreadInfo.getLockName()27* and ThreadInfo.getLockOwnerName()28* @author Mandy Chung29* @author Jaroslav Bachorik30*31* @library /test/lib32*33* @run main/othervm Locks34*/35import java.lang.management.*;36import java.util.Arrays;37import java.util.Objects;38import java.util.Optional;39import java.util.concurrent.Phaser;40import java.util.function.Predicate;41import jdk.test.lib.LockFreeLogger;4243public class Locks {4445private static final Object OBJA = new Object();46private static final Object OBJB = new Object();47private static final EnhancedWaiter OBJC = new EnhancedWaiter();48private static final ThreadMXBean TM = ManagementFactory.getThreadMXBean();49private static final LockFreeLogger LOGGER = new LockFreeLogger();5051private static String getLockName(Object lock) {52if (lock == null) return null;5354return lock.getClass().getName() + '@' +55Integer.toHexString(System.identityHashCode(lock));56}5758private static void assertNoLock(Thread t) {59if (t == null) {60return;61}62String name = t.getName();63Optional<ThreadInfo> result = Arrays.stream(64TM.getThreadInfo(TM.getAllThreadIds(), true, true))65.filter(Objects::nonNull)66.filter(i -> name.equals(i.getLockOwnerName()))67.findAny();68if (result.isPresent()) {69throw new RuntimeException("Thread " + t.getName() + " is not "70+ "supposed to be hold any lock. Currently owning lock : "71+ result.get().getLockName());72}73}7475/*76* Handy debug function to check if error condition is because of test code or not.77*/78private static void printStackTrace(Thread thread) {79if (thread == null) {80return;81}82StackTraceElement[] stackTrace = thread.getStackTrace();83log("Stack dump : Thread -> " + thread.getName());84for (StackTraceElement stackTraceEl : stackTrace) {85log("\t" + stackTraceEl.toString());86}87}8889private static void assertThreadState(Thread t, Thread.State expectedState) {90long tid = t.getId();91Thread.State actualState = TM.getThreadInfo(tid).getThreadState();92if (!actualState.equals(expectedState)) {93if (expectedState.equals(Thread.State.BLOCKED) ||94expectedState.equals(Thread.State.WAITING))95{96int retryCount = 0;97printStackTrace(t);98do {99goSleep(100);100actualState = TM.getThreadInfo(tid).getThreadState();101} while (!actualState.equals(expectedState) && retryCount++ <= 500);102}103if (!actualState.equals(expectedState)) {104printStackTrace(t);105throw new RuntimeException("Thread " + t.getName() + " is at "106+ actualState + " state but is expected to "107+ "be in Thread.State = " + expectedState);108}109}110}111112/*113* Do slow check if thread is blocked on a lock. It is possible that last thread114* to come out of Phaser might still be in Phaser call stack (Unsafe.park) and115* hence might eventually acquire expected lock.116*/117private static void checkBlockedObject(Thread t, Object lock, Thread owner) {118long tid = t.getId();119String result = TM.getThreadInfo(tid).getLockName();120final String expectedLock = (lock != null ? getLockName(lock) : null);121Predicate<String> p = (res) -> ((res != null && !res.equals(expectedLock))122|| (res == null && expectedLock != null));123124if (p.test(result)) {125printStackTrace(t);126int retryCount = 0;127while (p.test(result)) {128if (retryCount++ > 500) {129printStackTrace(t);130throw new RuntimeException("Thread " + t.getName() + " is blocked on "131+ expectedLock + " but got " + result);132}133goSleep(100);134result = TM.getThreadInfo(tid).getLockName();135}136}137138result = TM.getThreadInfo(tid).getLockOwnerName();139final String expectedOwner = (owner != null ? owner.getName() : null);140141p = (res) -> ((res != null && !res.equals(expectedOwner))142|| (res == null && expectedOwner != null));143if (p.test(result)) {144printStackTrace(t);145throw new RuntimeException("Owner of " + lock + " should be "146+ expectedOwner + " but got " + result);147}148}149150private static void goSleep(long ms){151try {152Thread.sleep(ms);153} catch (InterruptedException ex) {154throw new RuntimeException(ex);155}156}157158private static volatile int dummyCounter = 0;159160static class LockAThread extends Thread {161private final Phaser p;162public LockAThread(Phaser p) {163super("LockAThread");164this.p = p;165setDaemon(true);166}167@Override168public void run() {169synchronized(OBJA) {170// block here while LockBThread holds OBJB171log("LockAThread about to block on OBJB");172p.arriveAndAwaitAdvance(); // Phase 1 (blocking)173synchronized(OBJB) {174dummyCounter++;175}176}177p.arriveAndAwaitAdvance(); // Phase 2 (blocking)178log("LockAThread about to exit");179// Make sure the current thread is not holding any lock180assertNoLock(this);181}182}183184static class LockBThread extends Thread {185private final Phaser p;186public LockBThread(Phaser p) {187super("LockBThread");188this.p = p;189setDaemon(true);190}191@Override192public void run() {193synchronized(OBJB) {194log("LockBThread about to block on OBJC");195p.arriveAndAwaitAdvance(); // Phase 1 (blocking)196// Signal main thread about to block on OBJC197synchronized(OBJC) {198dummyCounter++;199}200}201p.arriveAndAwaitAdvance(); // Phase 2 (blocking)202log("LockBThread about to exit");203// Make sure the current thread is not holding any lock204assertNoLock(this);205}206}207208/*209* Must be invoked from within a synchronized context210*/211private static class EnhancedWaiter {212213boolean isNotified = false;214215public void doWait() throws InterruptedException {216while (!isNotified) {217wait();218}219isNotified = false;220}221222public void doNotify() {223isNotified = true;224notify();225}226}227228private static WaitingThread waiter;229private static final Object ready = new Object();230private static CheckerThread checker;231232static class WaitingThread extends Thread {233private final Phaser p;234235volatile boolean waiting = false;236237public WaitingThread(Phaser p) {238super("WaitingThread");239this.p = p;240setDaemon(true);241}242243@Override244public void run() {245try {246synchronized (OBJC) {247log("WaitingThread about to wait on OBJC");248// Signal checker thread, about to wait on OBJC.249waiting = false;250p.arriveAndAwaitAdvance(); // Phase 1 (waiting)251waiting = true;252OBJC.doWait();253254// block until CheckerThread finishes checking255log("WaitingThread about to block on ready");256// signal checker thread that it is about acquire257// object ready.258p.arriveAndAwaitAdvance(); // Phase 2 (waiting)259synchronized (ready) {260dummyCounter++;261}262}263synchronized (OBJC) {264// signal checker thread, about to wait on OBJC265waiting = false;266p.arriveAndAwaitAdvance(); // Phase 3 (waiting)267waiting = true;268OBJC.doWait();269}270log("WaitingThread about to exit waiting on OBJC 2");271} catch (InterruptedException e) {272// test failed and this thread was interrupted273}274}275276public void waitForWaiting() {277p.arriveAndAwaitAdvance();278while (!waiting) {279goSleep(10);280}281waitForState(State.WAITING);282}283284public void waitForBlocked() {285p.arriveAndAwaitAdvance();286waitForState(State.BLOCKED);287}288289private void waitForState(Thread.State state) {290while (!waiter.isInterrupted() && waiter.getState() != state) {291Thread.yield();292}293}294}295296static class CheckerThread extends Thread {297private Exception result = null;298299public CheckerThread() {300super("CheckerThread");301setDaemon(true);302}303304@Override305public void run() {306try {307synchronized (ready) {308// wait until WaitingThread about to wait for OBJC309waiter.waitForWaiting(); // Phase 1 (waiting)310assertThreadState(waiter, Thread.State.WAITING);311checkBlockedObject(waiter, OBJC, null);312synchronized (OBJC) {313OBJC.doNotify();314}315// wait for waiter thread to about to enter316// synchronized object ready.317waiter.waitForBlocked(); // Phase 2 (waiting)318assertThreadState(waiter, Thread.State.BLOCKED);319checkBlockedObject(waiter, ready, this);320}321322// wait for signal from waiting thread that it is about323// wait for OBJC.324waiter.waitForWaiting(); // Phase 3 (waiting)325synchronized (OBJC) {326assertThreadState(waiter, Thread.State.WAITING);327checkBlockedObject(waiter, OBJC, Thread.currentThread());328OBJC.doNotify();329}330} catch (Exception e) {331waiter.interrupt();332result = e;333}334}335336Exception result() {337return result;338}339}340341public static void main(String args[]) throws Exception {342try {343Thread mainThread = Thread.currentThread();344345// Test uncontested case346LockAThread t1;347LockBThread t2;348349Phaser p = new Phaser(3);350synchronized(OBJC) {351// Make sure the main thread is not holding any lock352assertNoLock(mainThread);353354// Test deadlock case355// t1 holds lockA and attempts to lock B356// t2 holds lockB and attempts to lock C357t1 = new LockAThread(p);358t1.start();359360t2 = new LockBThread(p);361t2.start();362363p.arriveAndAwaitAdvance(); // Phase 1 (blocking)364assertThreadState(t2, Thread.State.BLOCKED);365checkBlockedObject(t2, OBJC, mainThread);366assertThreadState(t1, Thread.State.BLOCKED);367checkBlockedObject(t1, OBJB, t2);368369long[] expectedThreads = new long[3];370expectedThreads[0] = t1.getId(); // blocked on lockB371expectedThreads[1] = t2.getId(); // owner of lockB blocking on lockC372expectedThreads[2] = mainThread.getId(); // owner of lockC373findThreadsBlockedOn(OBJB, expectedThreads);374}375p.arriveAndAwaitAdvance(); // Phase 2 (blocking)376377p = new Phaser(2);378// Test Object.wait() case379waiter = new WaitingThread(p);380waiter.start();381checker = new CheckerThread();382checker.start();383try {384waiter.join();385checker.join();386} catch (InterruptedException e) {387throw new RuntimeException(e);388}389if (checker.result() != null) {390throw checker.result();391}392} finally { // log all the messages to STDOUT393System.out.println(LOGGER.toString());394}395System.out.println("Test passed.");396}397398private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock)399throws Exception {400ThreadInfo ownerInfo = null;401for (ThreadInfo info : infos) {402String blockedLock = info.getLockName();403if (lock.equals(blockedLock)) {404long threadId = info.getLockOwnerId();405if (threadId == -1) {406throw new RuntimeException("TEST FAILED: " +407lock + " expected to have owner");408}409for (ThreadInfo info1 : infos) {410if (info1.getThreadId() == threadId) {411ownerInfo = info1;412break;413}414}415}416}417return ownerInfo;418}419private static void findThreadsBlockedOn(Object o, long[] expectedThreads)420throws Exception {421String lock = getLockName(o);422// Check with ThreadInfo with no stack trace (i.e. no safepoint)423ThreadInfo[] infos = TM.getThreadInfo(TM.getAllThreadIds());424doCheck(infos, lock, expectedThreads);425426// Check with ThreadInfo with stack trace427infos = TM.getThreadInfo(TM.getAllThreadIds(), 1);428doCheck(infos, lock, expectedThreads);429}430431private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThreads)432throws Exception {433ThreadInfo ownerInfo = null;434// Find the thread who is blocking on lock435for (ThreadInfo info : infos) {436String blockedLock = info.getLockName();437if (lock.equals(blockedLock)) {438log("%s blocked on %s", info.getThreadName(), blockedLock);439ownerInfo = info;440}441}442if (ownerInfo == null) {443throw new RuntimeException("TEST FAILED: " +444"Can't retrieve ThreadInfo for the blocked thread");445}446447long[] threads = new long[10];448int count = 0;449threads[count++] = ownerInfo.getThreadId();450while (ownerInfo.getThreadState() == Thread.State.BLOCKED) {451ownerInfo = findOwnerInfo(infos, lock);452threads[count++] = ownerInfo.getThreadId();453log(" Owner = %s id = %d",454ownerInfo.getThreadName(),455ownerInfo.getThreadId()456);457lock = ownerInfo.getLockName();458log("%s Id = %d blocked on %s",459ownerInfo.getThreadName(),460ownerInfo.getThreadId(),461lock462);463}464log("");465466if (count != expectedThreads.length) {467throw new RuntimeException("TEST FAILED: " +468"Expected chain of threads not matched; current count =" + count);469}470for (int i = 0; i < count; i++) {471if (threads[i] != expectedThreads[i]) {472log("TEST FAILED: Unexpected thread in the chain %s expected to be %s",473threads[i],474expectedThreads[i]475);476}477}478}479480private static void log(String format, Object ... args) {481LOGGER.log(format + "%n", args);482}483}484485486