8231032: ThreadMXBean locking tests fail after JSR 166 refresh

Reviewed-by: martin, mchung, dholmes
This commit is contained in:
Doug Lea 2019-09-27 12:20:14 -07:00
parent 3e3d90d6a1
commit a9254cbcfa
7 changed files with 160 additions and 6 deletions

@ -130,7 +130,7 @@ public abstract class AbstractQueuedLongSynchronizer
}
public final boolean block() {
while (!isReleasable()) LockSupport.park(this);
while (!isReleasable()) LockSupport.park();
return true;
}
}

@ -502,7 +502,7 @@ public abstract class AbstractQueuedSynchronizer
}
public final boolean block() {
while (!isReleasable()) LockSupport.park(this);
while (!isReleasable()) LockSupport.park();
return true;
}
}

@ -564,8 +564,6 @@ java/lang/management/ThreadMXBean/AllThreadIds.java 8131745 generic-
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

@ -76,6 +76,7 @@ import java.io.ByteArrayOutputStream;
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 @@ public class JSR166TestCase extends TestCase {
}
}
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 @@ public class JSR166TestCase extends TestCase {
}
}
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 ------");
@ -1187,6 +1190,17 @@ public class JSR166TestCase extends TestCase {
fail("timed out waiting for thread to enter thread state " + expected);
}
/**
* 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()}.
@ -1485,6 +1499,14 @@ public class JSR166TestCase extends TestCase {
return t;
}
/**
* 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
@ -1532,6 +1554,13 @@ public class JSR166TestCase extends TestCase {
}
}
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;

@ -39,10 +39,13 @@ import java.util.ArrayList;
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 @@ public class ReentrantLockTest extends JSR166TestCase {
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);
}
}

@ -38,6 +38,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
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 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase {
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);
}
}

@ -12,4 +12,6 @@ grant {
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";
};