8231032: ThreadMXBean locking tests fail after JSR 166 refresh
authordl
Fri, 27 Sep 2019 12:20:14 -0700
changeset 58385 489532b89775
parent 58384 9a3a700ca571
child 58386 693c1eb29b8d
8231032: ThreadMXBean locking tests fail after JSR 166 refresh Reviewed-by: martin, mchung, dholmes
src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
test/jdk/ProblemList.txt
test/jdk/java/util/concurrent/tck/JSR166TestCase.java
test/jdk/java/util/concurrent/tck/ReentrantLockTest.java
test/jdk/java/util/concurrent/tck/ReentrantReadWriteLockTest.java
test/jdk/java/util/concurrent/tck/tck.policy
--- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java	Fri Sep 27 10:48:23 2019 -0700
+++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java	Fri Sep 27 12:20:14 2019 -0700
@@ -130,7 +130,7 @@
         }
 
         public final boolean block() {
-            while (!isReleasable()) LockSupport.park(this);
+            while (!isReleasable()) LockSupport.park();
             return true;
         }
     }
--- a/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java	Fri Sep 27 10:48:23 2019 -0700
+++ b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java	Fri Sep 27 12:20:14 2019 -0700
@@ -502,7 +502,7 @@
         }
 
         public final boolean block() {
-            while (!isReleasable()) LockSupport.park(this);
+            while (!isReleasable()) LockSupport.park();
             return true;
         }
     }
--- a/test/jdk/ProblemList.txt	Fri Sep 27 10:48:23 2019 -0700
+++ b/test/jdk/ProblemList.txt	Fri Sep 27 12:20:14 2019 -0700
@@ -564,8 +564,6 @@
 javax/management/monitor/DerivedGaugeMonitorTest.java         8042211 generic-all
 javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java 8042215 generic-all
 
-java/lang/management/ThreadMXBean/LockedSynchronizers.java 8231032 generic-all
-
 ############################################################################
 
 # jdk_io
--- a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java	Fri Sep 27 10:48:23 2019 -0700
+++ b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java	Fri Sep 27 12:20:14 2019 -0700
@@ -76,6 +76,7 @@
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.lang.management.ManagementFactory;
+import java.lang.management.LockInfo;
 import java.lang.management.ThreadInfo;
 import java.lang.management.ThreadMXBean;
 import java.lang.reflect.Constructor;
@@ -270,6 +271,9 @@
         }
     }
 
+    private static final ThreadMXBean THREAD_MXBEAN
+        = ManagementFactory.getThreadMXBean();
+
     /**
      * The scaling factor to apply to standard delays used in tests.
      * May be initialized from any of:
@@ -1157,9 +1161,8 @@
             }
         }
 
-        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
         System.err.println("------ stacktrace dump start ------");
-        for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true))
+        for (ThreadInfo info : THREAD_MXBEAN.dumpAllThreads(true, true))
             if (threadOfInterest(info))
                 System.err.print(info);
         System.err.println("------ stacktrace dump end ------");
@@ -1188,6 +1191,17 @@
     }
 
     /**
+     * Returns the thread's blocker's class name, if any, else null.
+     */
+    String blockerClassName(Thread thread) {
+        ThreadInfo threadInfo; LockInfo lockInfo;
+        if ((threadInfo = THREAD_MXBEAN.getThreadInfo(thread.getId(), 0)) != null
+            && (lockInfo = threadInfo.getLockInfo()) != null)
+            return lockInfo.getClassName();
+        return null;
+    }
+
+    /**
      * Checks that future.get times out, with the default timeout of
      * {@code timeoutMillis()}.
      */
@@ -1486,6 +1500,14 @@
     }
 
     /**
+     * Returns a new started daemon Thread running the given action,
+     * wrapped in a CheckedRunnable.
+     */
+    Thread newStartedThread(Action action) {
+        return newStartedThread(checkedRunnable(action));
+    }
+
+    /**
      * Waits for the specified time (in milliseconds) for the thread
      * to terminate (using {@link Thread#join(long)}), else interrupts
      * the thread (in the hope that it may terminate later) and fails.
@@ -1532,6 +1554,13 @@
         }
     }
 
+    Runnable checkedRunnable(Action action) {
+        return new CheckedRunnable() {
+            public void realRun() throws Throwable {
+                action.run();
+            }};
+    }
+
     public abstract class ThreadShouldThrow extends Thread {
         protected abstract void realRun() throws Throwable;
 
--- a/test/jdk/java/util/concurrent/tck/ReentrantLockTest.java	Fri Sep 27 10:48:23 2019 -0700
+++ b/test/jdk/java/util/concurrent/tck/ReentrantLockTest.java	Fri Sep 27 12:20:14 2019 -0700
@@ -39,10 +39,13 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
 import junit.framework.Test;
@@ -1222,4 +1225,65 @@
             assertFalse(thread.isAlive());
         }
     }
+
+    /**
+     * ThreadMXBean reports the blockers that we expect.
+     */
+    public void testBlockers() {
+        if (!testImplementationDetails) return;
+        final boolean fair = randomBoolean();
+        final boolean timedAcquire = randomBoolean();
+        final boolean timedAwait = randomBoolean();
+        final String syncClassName = fair
+            ? "ReentrantLock$FairSync"
+            : "ReentrantLock$NonfairSync";
+        final String conditionClassName
+            = "AbstractQueuedSynchronizer$ConditionObject";
+        final Thread.State expectedAcquireState = timedAcquire
+            ? Thread.State.TIMED_WAITING
+            : Thread.State.WAITING;
+        final Thread.State expectedAwaitState = timedAwait
+            ? Thread.State.TIMED_WAITING
+            : Thread.State.WAITING;
+        final Lock lock = new ReentrantLock(fair);
+        final Condition condition = lock.newCondition();
+        final AtomicBoolean conditionSatisfied = new AtomicBoolean(false);
+        lock.lock();
+        final Thread thread = newStartedThread((Action) () -> {
+            if (timedAcquire)
+                lock.tryLock(LONGER_DELAY_MS, MILLISECONDS);
+            else
+                lock.lock();
+            while (!conditionSatisfied.get())
+                if (timedAwait)
+                    condition.await(LONGER_DELAY_MS, MILLISECONDS);
+                else
+                    condition.await();
+        });
+        Callable<Boolean> waitingForLock = () -> {
+            String className;
+            return thread.getState() == expectedAcquireState
+            && (className = blockerClassName(thread)) != null
+            && className.endsWith(syncClassName);
+        };
+        waitForThreadToEnterWaitState(thread, waitingForLock);
+
+        lock.unlock();
+        Callable<Boolean> waitingForCondition = () -> {
+            String className;
+            return thread.getState() == expectedAwaitState
+            && (className = blockerClassName(thread)) != null
+            && className.endsWith(conditionClassName);
+        };
+        waitForThreadToEnterWaitState(thread, waitingForCondition);
+
+        // politely release the waiter
+        conditionSatisfied.set(true);
+        lock.lock();
+        try {
+            condition.signal();
+        } finally { lock.unlock(); }
+
+        awaitTermination(thread);
+    }
 }
--- a/test/jdk/java/util/concurrent/tck/ReentrantReadWriteLockTest.java	Fri Sep 27 10:48:23 2019 -0700
+++ b/test/jdk/java/util/concurrent/tck/ReentrantReadWriteLockTest.java	Fri Sep 27 12:20:14 2019 -0700
@@ -38,6 +38,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.Condition;
@@ -1707,4 +1708,64 @@
         assertTrue(lock.writeLock().toString().contains("Unlocked"));
     }
 
+    /**
+     * ThreadMXBean reports the blockers that we expect.
+     */
+    public void testBlockers() {
+        if (!testImplementationDetails) return;
+        final boolean fair = randomBoolean();
+        final boolean timedAcquire = randomBoolean();
+        final boolean timedAwait = randomBoolean();
+        final String syncClassName = fair
+            ? "ReentrantReadWriteLock$FairSync"
+            : "ReentrantReadWriteLock$NonfairSync";
+        final String conditionClassName
+            = "AbstractQueuedSynchronizer$ConditionObject";
+        final Thread.State expectedAcquireState = timedAcquire
+            ? Thread.State.TIMED_WAITING
+            : Thread.State.WAITING;
+        final Thread.State expectedAwaitState = timedAwait
+            ? Thread.State.TIMED_WAITING
+            : Thread.State.WAITING;
+        final Lock lock = new ReentrantReadWriteLock(fair).writeLock();
+        final Condition condition = lock.newCondition();
+        final AtomicBoolean conditionSatisfied = new AtomicBoolean(false);
+        lock.lock();
+        final Thread thread = newStartedThread((Action) () -> {
+            if (timedAcquire)
+                lock.tryLock(LONGER_DELAY_MS, MILLISECONDS);
+            else
+                lock.lock();
+            while (!conditionSatisfied.get())
+                if (timedAwait)
+                    condition.await(LONGER_DELAY_MS, MILLISECONDS);
+                else
+                    condition.await();
+        });
+        Callable<Boolean> waitingForLock = () -> {
+            String className;
+            return thread.getState() == expectedAcquireState
+            && (className = blockerClassName(thread)) != null
+            && className.endsWith(syncClassName);
+        };
+        waitForThreadToEnterWaitState(thread, waitingForLock);
+
+        lock.unlock();
+        Callable<Boolean> waitingForCondition = () -> {
+            String className;
+            return thread.getState() == expectedAwaitState
+            && (className = blockerClassName(thread)) != null
+            && className.endsWith(conditionClassName);
+        };
+        waitForThreadToEnterWaitState(thread, waitingForCondition);
+
+        // politely release the waiter
+        conditionSatisfied.set(true);
+        lock.lock();
+        try {
+            condition.signal();
+        } finally { lock.unlock(); }
+
+        awaitTermination(thread);
+    }
 }
--- a/test/jdk/java/util/concurrent/tck/tck.policy	Fri Sep 27 10:48:23 2019 -0700
+++ b/test/jdk/java/util/concurrent/tck/tck.policy	Fri Sep 27 12:20:14 2019 -0700
@@ -12,4 +12,6 @@
     permission java.lang.RuntimePermission "accessDeclaredMembers";
     permission java.io.FilePermission "<<ALL FILES>>", "read";
     permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+    // Allows test methods to inspect test thread state
+    permission java.lang.management.ManagementPermission "monitor";
 };