8031701: java/lang/management/ThreadMXBean/Locks.java: Thread WaitingThread is expected to wait on Object but got null Thread.State = RUNNABLE
authorjbachorik
Wed, 29 Jan 2014 17:37:40 +0100
changeset 22612 92f9ded70a69
parent 22611 4dd8f4f14e2c
child 22613 4492f0cfdd38
8031701: java/lang/management/ThreadMXBean/Locks.java: Thread WaitingThread is expected to wait on Object but got null Thread.State = RUNNABLE Reviewed-by: mchung, dsamersoff
jdk/test/java/lang/management/ThreadMXBean/Locks.java
jdk/test/java/lang/management/ThreadMXBean/ThreadExecutionSynchronizer.java
--- a/jdk/test/java/lang/management/ThreadMXBean/Locks.java	Wed Jan 29 13:10:53 2014 +0100
+++ b/jdk/test/java/lang/management/ThreadMXBean/Locks.java	Wed Jan 29 17:37:40 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,18 +27,19 @@
  * @summary Basic unit test of ThreadInfo.getLockName()
  *          and ThreadInfo.getLockOwnerName()
  * @author  Mandy Chung
+ * @author  Jaroslav Bachorik
  *
- * @build ThreadExecutionSynchronizer
  * @run main/othervm Locks
  */
 
 import java.lang.management.*;
+import java.util.concurrent.Phaser;
 
 public class Locks {
-    private static Object objA = new Object();
-    private static Object objB = new Object();
-    private static Object objC = new Object();
-    private static ThreadMXBean tm = ManagementFactory.getThreadMXBean();
+    private static final Object objA = new Object();
+    private static final Object objB = new Object();
+    private static final Object objC = new Object();
+    private static final ThreadMXBean tm = ManagementFactory.getThreadMXBean();
 
     private static boolean testFailed = false;
 
@@ -46,48 +47,62 @@
         if (lock == null) return null;
 
         return lock.getClass().getName() + '@' +
-            Integer.toHexString(System.identityHashCode(lock));
+                Integer.toHexString(System.identityHashCode(lock));
+    }
+
+    private static void assertNoLock(Thread t) {
+        long tid = t.getId();
+        ThreadInfo info = tm.getThreadInfo(tid);
+        String result = info.getLockName();
+
+        if (result != null) {
+            throw new RuntimeException("Thread " + t.getName() + " is not supposed to hold any lock. " +
+                                       "Currently owning lock: " + result);
+        }
     }
 
     private static void checkBlockedObject(Thread t, Object lock, Thread owner,
                                            Thread.State expectedState) {
-        ThreadInfo info = tm.getThreadInfo(t.getId());
+        long tid = t.getId();
+        ThreadInfo info = tm.getThreadInfo(tid);
         String result = info.getLockName();
         String expectedLock = (lock != null ? getLockName(lock) : null);
         String expectedOwner = (owner != null ? owner.getName() : null);
 
         if (lock != null) {
-            if (expectedState ==Thread.State.BLOCKED) {
+            if (expectedState == Thread.State.BLOCKED) {
                 int retryCount=0;
                 while(info.getThreadState() != Thread.State.BLOCKED) {
                     if (retryCount++ > 500) {
                         throw new RuntimeException("Thread " + t.getName() +
-                                  " is expected to block on " + expectedLock +
-                                  " but got " + result +
-                                  " Thread.State = " + info.getThreadState());
+                                " is expected to block on " + expectedLock +
+                                " but got " + result +
+                                " Thread.State = " + info.getThreadState());
                     }
                     goSleep(100);
+                    info = tm.getThreadInfo(tid);
+                    result = info.getLockName();
                 }
             }
             if (expectedState == Thread.State.WAITING &&
-                info.getThreadState() != Thread.State.WAITING) {
+                    info.getThreadState() != Thread.State.WAITING) {
                 throw new RuntimeException("Thread " + t.getName() +
-                    " is expected to wait on " + expectedLock +
-                    " but got " + result +
-                    " Thread.State = " + info.getThreadState());
+                        " is expected to wait on " + expectedLock +
+                        " but got " + result +
+                        " Thread.State = " + info.getThreadState());
             }
         }
 
         if ((result != null && !result.equals(expectedLock)) ||
-            (result == null && expectedLock != null)) {
+                (result == null && expectedLock != null)) {
             throw new RuntimeException("Thread " + t.getName() + " is blocked on " +
-                expectedLock + " but got " + result);
+                    expectedLock + " but got " + result);
         }
         result = info.getLockOwnerName();
         if ((result != null && !result.equals(expectedOwner)) ||
-            (result == null && expectedOwner != null)) {
+                (result == null && expectedOwner != null)) {
             throw new RuntimeException("Owner of " + lock + " should be " +
-                expectedOwner + " but got " + result);
+                    expectedOwner + " but got " + result);
         }
     }
 
@@ -100,53 +115,49 @@
         }
     }
 
-    static ThreadExecutionSynchronizer thrsync = new ThreadExecutionSynchronizer();
-    static ThreadExecutionSynchronizer thrsync1 = new ThreadExecutionSynchronizer();
+    private static volatile int dummyCounter = 0;
 
     static class LockAThread extends Thread {
-        public LockAThread() {
+        private final Phaser p;
+        public LockAThread(Phaser p) {
             super("LockAThread");
+            this.p = p;
         }
         public void run() {
             synchronized(objA) {
-               // stop here  for LockBThread to hold objB
-               thrsync.waitForSignal();
-
-               System.out.println("LockAThread about to block on objB");
-               synchronized(objB) {};
+                // stop here  for LockBThread to hold objB
+                System.out.println("LockAThread about to block on objB");
+                p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
+                synchronized(objB) {
+                    dummyCounter++;
+                };
             }
+            p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
             System.out.println("LockAThread about to exit");
-            // The state could be anything. The expected state value
-            // passed with this method is not verified.
-            checkBlockedObject(this, null, null, Thread.State.TERMINATED);
+            // Make sure the current thread is not holding any lock
+            assertNoLock(this);
         }
     }
 
     static class LockBThread extends Thread {
-        public LockBThread() {
+        private final Phaser p;
+        public LockBThread(Phaser p) {
             super("LockBThread");
+            this.p = p;
         }
         public void run() {
             synchronized(objB) {
-               // signal waiting LockAThread.
-               thrsync.signal();
-
-               System.out.println("LockBThread about to block on objC");
-               // Signal main thread about to block on objC
-               thrsync1.signal();
-               synchronized(objC) {};
+                System.out.println("LockBThread about to block on objC");
+                p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
+                // Signal main thread about to block on objC
+                synchronized(objC) {
+                    dummyCounter++;
+                };
             }
+            p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
             System.out.println("LockBThread about to exit");
-            // The state could be anything. The expected state value
-            // passed with this method is not verified.
-            checkBlockedObject(this, null, null, Thread.State.TERMINATED);
-        }
-
-        public void aboutToLockC() {
-            // Stop here till LockBThread about to blocked
-            // for lock objC.
-            thrsync1.waitForSignal();
-            goSleep(500);
+            // Make sure the current thread is not holding any lock
+            assertNoLock(this);
         }
     }
 
@@ -154,32 +165,36 @@
     private static Object ready = new Object();
     private static CheckerThread checker;
     static class WaitingThread extends Thread {
-        public WaitingThread() {
+        private final Phaser p;
+        public WaitingThread(Phaser p) {
             super("WaitingThread");
+            this.p = p;
         }
         public void run() {
             synchronized(objC) {
-               System.out.println("WaitingThread about to wait on objC");
-               try {
-                   // Signal checker thread, about to wait on objC.
-                   thrsync.signal();
-                   objC.wait();
-               } catch (InterruptedException e) {
-                   e.printStackTrace();
-                   testFailed = true;
-               }
+                System.out.println("WaitingThread about to wait on objC");
+                try {
+                    // Signal checker thread, about to wait on objC.
+                    p.arriveAndAwaitAdvance(); // Phase 1 (waiting)
+                    objC.wait();
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                    testFailed = true;
+                }
 
-               // block until CheckerThread finishes checking
-               System.out.println("WaitingThread about to block on ready");
-               // signal checker thread that it is about acquire
-               // object ready.
-               thrsync.signal();
-               synchronized(ready) {};
+                // block until CheckerThread finishes checking
+                System.out.println("WaitingThread about to block on ready");
+                // signal checker thread that it is about acquire
+                // object ready.
+                p.arriveAndAwaitAdvance(); // Phase 2 (waiting)
+                synchronized(ready) {
+                    dummyCounter++;
+                };
             }
             synchronized(objC) {
                 try {
                     // signal checker thread, about to wait on objC
-                    thrsync.signal();
+                    p.arriveAndAwaitAdvance(); // Phase 3 (waiting)
                     objC.wait();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
@@ -190,21 +205,23 @@
         }
     }
     static class CheckerThread extends Thread {
-        public CheckerThread() {
+        private final Phaser p;
+        public CheckerThread(Phaser p) {
             super("CheckerThread");
+            this.p = p;
         }
 
         private void waitForState(Thread.State state) {
-            thrsync.waitForSignal();
-            while (waiter.getState() != state) {
-               goSleep(10);
+            p.arriveAndAwaitAdvance();
+            while (!waiter.isInterrupted() && waiter.getState() != state) {
+                goSleep(10);
             }
         }
 
         public void run() {
             synchronized (ready) {
                 // wait until WaitingThread about to wait for objC
-                waitForState(Thread.State.WAITING);
+                waitForState(Thread.State.WAITING); // Phase 1 (waiting)
                 checkBlockedObject(waiter, objC, null, Thread.State.WAITING);
 
                 synchronized (objC) {
@@ -213,13 +230,13 @@
 
                 // wait for waiter thread to about to enter
                 // synchronized object ready.
-                waitForState(Thread.State.BLOCKED);
+                waitForState(Thread.State.BLOCKED); // Phase 2 (waiting)
                 checkBlockedObject(waiter, ready, this, Thread.State.BLOCKED);
             }
 
             // wait for signal from waiting thread that it is about
             // wait for objC.
-            waitForState(Thread.State.WAITING);
+            waitForState(Thread.State.WAITING); // Phase 3 (waiting)
             synchronized(objC) {
                 checkBlockedObject(waiter, objC, Thread.currentThread(), Thread.State.WAITING);
                 objC.notify();
@@ -235,24 +252,24 @@
         LockAThread t1;
         LockBThread t2;
 
+        Phaser p = new Phaser(3);
         synchronized(objC) {
-            // The state could be anything. The expected state value
-            // passed with this method is not verified.
-            checkBlockedObject(mainThread, null, null, Thread.State.RUNNABLE);
+            // Make sure the main thread is not holding any lock
+            assertNoLock(mainThread);
 
             // Test deadlock case
             // t1 holds lockA and attempts to lock B
             // t2 holds lockB and attempts to lock C
-            t1 = new LockAThread();
+
+            t1 = new LockAThread(p);
             t1.start();
 
-            t2 = new LockBThread();
+            t2 = new LockBThread(p);
             t2.start();
 
-            t2.aboutToLockC();
-
+            p.arriveAndAwaitAdvance(); // Phase 1 (blocking)
+            checkBlockedObject(t2, objC, mainThread, Thread.State.BLOCKED);
             checkBlockedObject(t1, objB, t2, Thread.State.BLOCKED);
-            checkBlockedObject(t2, objC, mainThread, Thread.State.BLOCKED);
 
             long[] expectedThreads = new long[3];
             expectedThreads[0] = t1.getId(); // blocked on lockB
@@ -260,13 +277,14 @@
             expectedThreads[2] = mainThread.getId(); // owner of lockC
             findThreadsBlockedOn(objB, expectedThreads);
         }
-        goSleep(100);
+        p.arriveAndAwaitAdvance(); // Phase 2 (blocking)
 
+        p = new Phaser(2);
         // Test Object.wait() case
-        waiter = new WaitingThread();
+        waiter = new WaitingThread(p);
         waiter.start();
 
-        checker = new CheckerThread();
+        checker = new CheckerThread(p);
         checker.start();
 
         try {
@@ -284,7 +302,7 @@
     }
 
     private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock)
-        throws Exception {
+            throws Exception {
         ThreadInfo ownerInfo = null;
         for (int i = 0; i < infos.length; i++) {
             String blockedLock = infos[i].getLockName();
@@ -292,7 +310,7 @@
                 long threadId = infos[i].getLockOwnerId();
                 if (threadId == -1) {
                     throw new RuntimeException("TEST FAILED: " +
-                        lock + " expected to have owner");
+                            lock + " expected to have owner");
                 }
                 for (int j = 0; j < infos.length; j++) {
                     if (infos[j].getThreadId() == threadId) {
@@ -305,7 +323,7 @@
         return ownerInfo;
     }
     private static void findThreadsBlockedOn(Object o, long[] expectedThreads)
-        throws Exception {
+            throws Exception {
         String lock = getLockName(o);
         // Check with ThreadInfo with no stack trace (i.e. no safepoint)
         ThreadInfo[] infos = tm.getThreadInfo(tm.getAllThreadIds());
@@ -317,14 +335,14 @@
     }
 
     private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThreads)
-        throws Exception {
+            throws Exception {
         ThreadInfo ownerInfo = null;
         // Find the thread who is blocking on lock
         for (int i = 0; i < infos.length;  i++) {
             String blockedLock = infos[i].getLockName();
             if (lock.equals(blockedLock)) {
                 System.out.print(infos[i].getThreadName() +
-                    " blocked on " + blockedLock);
+                        " blocked on " + blockedLock);
                 ownerInfo = infos[i];
             }
         }
@@ -336,7 +354,7 @@
             ownerInfo = findOwnerInfo(infos, lock);
             threads[count++] = ownerInfo.getThreadId();
             System.out.println(" Owner = " + ownerInfo.getThreadName() +
-                " id = " + ownerInfo.getThreadId());
+                    " id = " + ownerInfo.getThreadId());
             lock = ownerInfo.getLockName();
             System.out.print(ownerInfo.getThreadName() + " Id = " +
                     ownerInfo.getThreadId() +
@@ -346,13 +364,13 @@
 
         if (count != expectedThreads.length) {
             throw new RuntimeException("TEST FAILED: " +
-                "Expected chain of threads not matched; current count =" + count);
+                    "Expected chain of threads not matched; current count =" + count);
         }
         for (int i = 0; i < count; i++) {
             if (threads[i] != expectedThreads[i]) {
                 System.out.println("TEST FAILED: " +
-                    "Unexpected thread in the chain " + threads[i] +
-                    " expected to be " + expectedThreads[i]);
+                        "Unexpected thread in the chain " + threads[i] +
+                        " expected to be " + expectedThreads[i]);
             }
         }
     }
--- a/jdk/test/java/lang/management/ThreadMXBean/ThreadExecutionSynchronizer.java	Wed Jan 29 13:10:53 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- *
- * @summary This class is used to synchronize execution of two threads.
- * @author  Swamy Venkataramanappa
- */
-
-import java.util.concurrent.Semaphore;
-
-public class ThreadExecutionSynchronizer {
-
-    private volatile boolean waiting;
-    private final Semaphore semaphore;
-
-    public ThreadExecutionSynchronizer() {
-        semaphore = new Semaphore(1);
-        waiting = false;
-    }
-
-    // Synchronizes two threads execution points.
-    // Basically any thread could get scheduled to run and
-    // it is not possible to know which thread reaches expected
-    // execution point. So whichever thread reaches a execution
-    // point first wait for the second thread. When the second thread
-    // reaches the expected execution point will wake up
-    // the thread which is waiting here.
-    void stopOrGo() {
-        semaphore.acquireUninterruptibly(); // Thread can get blocked.
-        if (!waiting) {
-            waiting = true;
-            // Wait for second thread to enter this method.
-            while(!semaphore.hasQueuedThreads()) {
-                try {
-                    Thread.sleep(20);
-                } catch (InterruptedException xx) {}
-            }
-            semaphore.release();
-        } else {
-            waiting = false;
-            semaphore.release();
-        }
-    }
-
-    // Wrapper function just for code readability.
-    void waitForSignal() {
-        stopOrGo();
-        goSleep(50);
-    }
-
-    void signal() {
-        stopOrGo();
-        goSleep(50);
-    }
-
-    private static void goSleep(long ms) {
-        try {
-            Thread.sleep(ms);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-            System.out.println("Unexpected exception.");
-        }
-    }
-}