8231032: ThreadMXBean locking tests fail after JSR 166 refresh
Reviewed-by: martin, mchung, dholmes
--- 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";
};