8240340: java/lang/management/ThreadMXBean/Locks.java is buggy
Reviewed-by: dholmes, sspitsyn
This commit is contained in:
parent
db69852ac2
commit
5531199db4
test
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2020, 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
|
||||
@ -89,25 +89,22 @@ public class Locks {
|
||||
|
||||
private static void assertThreadState(Thread t, Thread.State expectedState) {
|
||||
long tid = t.getId();
|
||||
if (expectedState == Thread.State.BLOCKED
|
||||
&& TM.getThreadInfo(tid).getThreadState() != Thread.State.BLOCKED) {
|
||||
int retryCount = 0;
|
||||
printStackTrace(t);
|
||||
while (TM.getThreadInfo(tid).getThreadState() != Thread.State.BLOCKED) {
|
||||
if (retryCount++ > 500) {
|
||||
printStackTrace(t);
|
||||
throw new RuntimeException("Thread " + t.getName() + " is at "
|
||||
+ TM.getThreadInfo(tid).getThreadState() + " state but is expected to "
|
||||
+ "be in Thread.State = " + expectedState);
|
||||
}
|
||||
goSleep(100);
|
||||
Thread.State actualState = TM.getThreadInfo(tid).getThreadState();
|
||||
if (!actualState.equals(expectedState)) {
|
||||
if (expectedState.equals(Thread.State.BLOCKED)) {
|
||||
int retryCount = 0;
|
||||
printStackTrace(t);
|
||||
do {
|
||||
goSleep(100);
|
||||
actualState = TM.getThreadInfo(tid).getThreadState();
|
||||
} while (!actualState.equals(expectedState) && retryCount++ <= 500);
|
||||
}
|
||||
if (!actualState.equals(expectedState)) {
|
||||
printStackTrace(t);
|
||||
throw new RuntimeException("Thread " + t.getName() + " is at "
|
||||
+ actualState + " state but is expected to "
|
||||
+ "be in Thread.State = " + expectedState);
|
||||
}
|
||||
}
|
||||
if (!TM.getThreadInfo(tid).getThreadState().equals(expectedState)) {
|
||||
printStackTrace(t);
|
||||
throw new RuntimeException("Thread " + t.getName() + " is at "
|
||||
+ TM.getThreadInfo(tid).getThreadState() + " state but is expected to "
|
||||
+ "be in Thread.State = " + expectedState);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,6 +161,7 @@ public class Locks {
|
||||
public LockAThread(Phaser p) {
|
||||
super("LockAThread");
|
||||
this.p = p;
|
||||
setDaemon(true);
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
@ -187,6 +185,7 @@ public class Locks {
|
||||
public LockBThread(Phaser p) {
|
||||
super("LockBThread");
|
||||
this.p = p;
|
||||
setDaemon(true);
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
@ -237,42 +236,40 @@ public class Locks {
|
||||
public WaitingThread(Phaser p) {
|
||||
super("WaitingThread");
|
||||
this.p = p;
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized(OBJC) {
|
||||
log("WaitingThread about to wait on OBJC");
|
||||
try {
|
||||
try {
|
||||
synchronized (OBJC) {
|
||||
log("WaitingThread about to wait on OBJC");
|
||||
// Signal checker thread, about to wait on OBJC.
|
||||
waiting = false;
|
||||
p.arriveAndAwaitAdvance(); // Phase 1 (waiting)
|
||||
waiting = true;
|
||||
OBJC.doWait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e); // Do not continue test
|
||||
}
|
||||
|
||||
// block until CheckerThread finishes checking
|
||||
log("WaitingThread about to block on ready");
|
||||
// signal checker thread that it is about acquire
|
||||
// object ready.
|
||||
p.arriveAndAwaitAdvance(); // Phase 2 (waiting)
|
||||
synchronized(ready) {
|
||||
dummyCounter++;
|
||||
// block until CheckerThread finishes checking
|
||||
log("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 {
|
||||
synchronized (OBJC) {
|
||||
// signal checker thread, about to wait on OBJC
|
||||
waiting = false;
|
||||
p.arriveAndAwaitAdvance(); // Phase 3 (waiting)
|
||||
waiting = true;
|
||||
OBJC.doWait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
log("WaitingThread about to exit waiting on OBJC 2");
|
||||
} catch (InterruptedException e) {
|
||||
// test failed and this thread was interrupted
|
||||
}
|
||||
log("WaitingThread about to exit waiting on OBJC 2");
|
||||
}
|
||||
|
||||
public void waitForWaiting() {
|
||||
@ -294,37 +291,49 @@ public class Locks {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class CheckerThread extends Thread {
|
||||
private Exception result = null;
|
||||
|
||||
public CheckerThread() {
|
||||
super("CheckerThread");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized(ready) {
|
||||
// wait until WaitingThread about to wait for OBJC
|
||||
waiter.waitForWaiting(); // Phase 1 (waiting)
|
||||
assertThreadState(waiter, Thread.State.WAITING);
|
||||
checkBlockedObject(waiter, OBJC, null);
|
||||
try {
|
||||
synchronized (ready) {
|
||||
// wait until WaitingThread about to wait for OBJC
|
||||
waiter.waitForWaiting(); // Phase 1 (waiting)
|
||||
assertThreadState(waiter, Thread.State.WAITING);
|
||||
checkBlockedObject(waiter, OBJC, null);
|
||||
synchronized (OBJC) {
|
||||
OBJC.doNotify();
|
||||
}
|
||||
// wait for waiter thread to about to enter
|
||||
// synchronized object ready.
|
||||
waiter.waitForBlocked(); // Phase 2 (waiting)
|
||||
assertThreadState(waiter, Thread.State.BLOCKED);
|
||||
checkBlockedObject(waiter, ready, this);
|
||||
}
|
||||
|
||||
synchronized(OBJC) {
|
||||
// wait for signal from waiting thread that it is about
|
||||
// wait for OBJC.
|
||||
waiter.waitForWaiting(); // Phase 3 (waiting)
|
||||
synchronized (OBJC) {
|
||||
assertThreadState(waiter, Thread.State.WAITING);
|
||||
checkBlockedObject(waiter, OBJC, Thread.currentThread());
|
||||
OBJC.doNotify();
|
||||
}
|
||||
// wait for waiter thread to about to enter
|
||||
// synchronized object ready.
|
||||
waiter.waitForBlocked(); // Phase 2 (waiting)
|
||||
assertThreadState(waiter, Thread.State.BLOCKED);
|
||||
checkBlockedObject(waiter, ready, this);
|
||||
} catch (Exception e) {
|
||||
waiter.interrupt();
|
||||
result = e;
|
||||
}
|
||||
}
|
||||
|
||||
// wait for signal from waiting thread that it is about
|
||||
// wait for OBJC.
|
||||
waiter.waitForWaiting(); // Phase 3 (waiting)
|
||||
synchronized(OBJC) {
|
||||
assertThreadState(waiter, Thread.State.WAITING);
|
||||
checkBlockedObject(waiter, OBJC, Thread.currentThread());
|
||||
OBJC.doNotify();
|
||||
}
|
||||
Exception result() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,16 +377,17 @@ public class Locks {
|
||||
// Test Object.wait() case
|
||||
waiter = new WaitingThread(p);
|
||||
waiter.start();
|
||||
|
||||
checker = new CheckerThread();
|
||||
checker.start();
|
||||
|
||||
try {
|
||||
waiter.join();
|
||||
checker.join();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (checker.result() != null) {
|
||||
throw checker.result();
|
||||
}
|
||||
} finally { // log all the messages to STDOUT
|
||||
System.out.println(LOGGER.toString());
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2020, 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
|
||||
@ -23,12 +23,8 @@
|
||||
|
||||
package jdk.test.lib;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -38,47 +34,32 @@ import java.util.stream.Collectors;
|
||||
* It is particularly useful in situations when one needs to assert various
|
||||
* details about the tested thread state or the locks it hold while also wanting
|
||||
* to produce diagnostic log messages.
|
||||
* <p>
|
||||
* The logger does not provide any guarantees about the completness of the
|
||||
* logs written from different threads - it is up to the caller to make sure
|
||||
* {@code toString()} method is called only when all the activity has ceased
|
||||
* and the per-thread logs contain all the necessary data.
|
||||
*
|
||||
* @author Jaroslav Bachorik
|
||||
**/
|
||||
*/
|
||||
public class LockFreeLogger {
|
||||
private final AtomicInteger logCntr = new AtomicInteger(0);
|
||||
private final Collection<Map<Integer, String>> allRecords = new ConcurrentLinkedQueue<>();
|
||||
private final ThreadLocal<Map<Integer, String>> records = ThreadLocal.withInitial(ConcurrentHashMap::new);
|
||||
/**
|
||||
* ConcurrentLinkedQueue implements non-blocking algorithm.
|
||||
*/
|
||||
private final Queue<String> records = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public LockFreeLogger() {
|
||||
allRecords.add(records.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
* Logs a message.
|
||||
* @param format Message format
|
||||
* @param params Message parameters
|
||||
*/
|
||||
public void log(String format, Object ... params) {
|
||||
int id = logCntr.getAndIncrement();
|
||||
records.get().put(id, String.format(format, params));
|
||||
records.add(String.format(format, params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Will generate an aggregated log of chronologically ordered messages.
|
||||
* <p>
|
||||
* Make sure that you call this method only when all the related threads
|
||||
* have finished; otherwise you might get incomplete data.
|
||||
* Generates an aggregated log of chronologically ordered messages.
|
||||
*
|
||||
* @return An aggregated log of chronologically ordered messages
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return allRecords.stream()
|
||||
.flatMap(m -> m.entrySet().stream())
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.map(Map.Entry::getValue)
|
||||
.collect(Collectors.joining());
|
||||
return records.stream().collect(Collectors.joining());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user