3789983e89
Reviewed-by: darcy, ihse
359 lines
13 KiB
Java
359 lines
13 KiB
Java
/*
|
|
* Copyright (c) 2005, 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.
|
|
*/
|
|
|
|
/*
|
|
* @bug 5086470 6358247
|
|
* @summary SynchronizersLockingThread is used by LockedSynchronizers.
|
|
* It will create threads that acquire ReentrantLock and also object
|
|
* monitors.
|
|
* @author Mandy Chung
|
|
*
|
|
* @build ThreadDump Utils
|
|
*/
|
|
|
|
import java.lang.management.*;
|
|
import java.util.*;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
import java.util.concurrent.locks.Condition;
|
|
|
|
public class SynchronizerLockingThread extends Thread {
|
|
static ReentrantLock lock1 = new ReentrantLock();
|
|
static ReentrantLock lock2 = new ReentrantLock();
|
|
static ReentrantLock lock3 = new ReentrantLock();
|
|
static ReentrantLock lock4 = new ReentrantLock();
|
|
static Lock lock5 = new Lock("lock5");
|
|
static Lock lock6 = new Lock("lock6");
|
|
static Lock lock7 = new Lock("lock7");
|
|
static ReentrantLock lock8 = new ReentrantLock();
|
|
|
|
static SynchronizerLockingThread t1 = new Thread1();
|
|
static SynchronizerLockingThread t2 = new Thread2();
|
|
static int count = 2;
|
|
static void startLockingThreads() {
|
|
t1.setDaemon(true);
|
|
t2.setDaemon(true);
|
|
t1.start();
|
|
t2.start();
|
|
|
|
// wait until t1 and t2 waits
|
|
while (count != 0) {
|
|
try {
|
|
Thread.sleep(100);
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
Utils.waitForBlockWaitingState(t1);
|
|
Utils.waitForBlockWaitingState(t2);
|
|
}
|
|
|
|
static long[] getThreadIds() {
|
|
return new long[] {t1.getId(), t2.getId()};
|
|
}
|
|
|
|
static void checkLocks(ThreadInfo[] tinfos) throws Exception {
|
|
int matches = 0;
|
|
for (ThreadInfo info : tinfos) {
|
|
if (info.getThreadId() == t1.getId()) {
|
|
t1.checkLocks(info);
|
|
matches++;
|
|
}
|
|
if (info.getThreadId() == t2.getId()) {
|
|
t2.checkLocks(info);
|
|
matches++;
|
|
}
|
|
}
|
|
if (matches != 2) {
|
|
throw new RuntimeException("MonitorInfo missing");
|
|
}
|
|
}
|
|
|
|
static class Lock {
|
|
String name;
|
|
Lock(String name) {
|
|
this.name = name;
|
|
}
|
|
public String toString() {
|
|
return name;
|
|
}
|
|
}
|
|
|
|
final String threadName;
|
|
Lock waitingLock;
|
|
int numOwnedMonitors;
|
|
Map<String, Lock[]> ownedMonitors;
|
|
Condition waitingSync;
|
|
int numOwnedSyncs;
|
|
Map<String, ReentrantLock[]> ownedSyncs;
|
|
public SynchronizerLockingThread(String name) {
|
|
this.threadName = name;
|
|
}
|
|
|
|
protected void setExpectedResult(Lock waitingLock,
|
|
int numOwnedMonitors,
|
|
Map<String, Lock[]> ownedMonitors,
|
|
Condition waitingSync,
|
|
int numOwnedSyncs,
|
|
Map<String, ReentrantLock[]> ownedSyncs) {
|
|
this.waitingLock = waitingLock;
|
|
this.numOwnedMonitors = numOwnedMonitors;
|
|
this.ownedMonitors = ownedMonitors;
|
|
this.waitingSync = waitingSync;
|
|
this.numOwnedSyncs = numOwnedSyncs;
|
|
this.ownedSyncs = ownedSyncs;
|
|
}
|
|
|
|
void checkLocks(ThreadInfo info) throws Exception {
|
|
checkThreadInfo(info);
|
|
MonitorInfo[] monitors = info.getLockedMonitors();
|
|
if (monitors.length != numOwnedMonitors) {
|
|
ThreadDump.threadDump();
|
|
throw new RuntimeException("Number of locked monitors = " +
|
|
monitors.length +
|
|
" not matched. Expected: " + numOwnedMonitors);
|
|
}
|
|
// check if each monitor returned in the list is the expected
|
|
// one
|
|
for (MonitorInfo m : monitors) {
|
|
StackTraceElement ste = m.getLockedStackFrame();
|
|
int depth = m.getLockedStackDepth();
|
|
checkStackFrame(info, ste, depth);
|
|
checkMonitor(m, ste.getMethodName());
|
|
}
|
|
// check if each expected monitor is included in the returned
|
|
// list
|
|
for (Map.Entry<String, Lock[]> e : ownedMonitors.entrySet()) {
|
|
for (Lock l : e.getValue()) {
|
|
checkMonitor(e.getKey(), l, monitors);
|
|
}
|
|
}
|
|
|
|
// We can only check if the length matches since we have no
|
|
// way to get the AbstractOwnableSynchronizer in ReentrantLock
|
|
LockInfo[] syncs = info.getLockedSynchronizers();
|
|
if (syncs.length != numOwnedSyncs) {
|
|
ThreadDump.threadDump();
|
|
throw new RuntimeException("Number of locked syncs = " +
|
|
syncs.length +
|
|
" not matched. Expected: " + numOwnedSyncs);
|
|
}
|
|
}
|
|
|
|
void checkThreadInfo(ThreadInfo info) throws Exception {
|
|
if (!getName().equals(info.getThreadName())) {
|
|
throw new RuntimeException("Name: " + info.getThreadName() +
|
|
" not matched. Expected: " + getName());
|
|
}
|
|
LockInfo l = info.getLockInfo();
|
|
if ((waitingLock != null || waitingSync != null) && l == null) {
|
|
throw new RuntimeException("LockInfo: " + l +
|
|
" not matched. Expected: non-null");
|
|
}
|
|
if (waitingLock == null && waitingSync == null && l != null) {
|
|
throw new RuntimeException("LockInfo: " + l +
|
|
" not matched. Expected: null");
|
|
}
|
|
|
|
String waitingLockName;
|
|
int hcode;
|
|
if (waitingLock != null) {
|
|
waitingLockName = waitingLock.getClass().getName();
|
|
hcode = System.identityHashCode(waitingLock);
|
|
} else {
|
|
waitingLockName = waitingSync.getClass().getName();
|
|
hcode = System.identityHashCode(waitingSync);
|
|
}
|
|
if (!waitingLockName.equals(l.getClassName())) {
|
|
throw new RuntimeException("LockInfo : " + l +
|
|
" class name not matched. Expected: " + waitingLockName);
|
|
}
|
|
if (hcode != l.getIdentityHashCode()) {
|
|
throw new RuntimeException("LockInfo: " + l +
|
|
" IdentityHashCode not matched. Expected: " + hcode);
|
|
}
|
|
|
|
String lockName = info.getLockName();
|
|
String[] s = lockName.split("@");
|
|
if (!waitingLockName.equals(s[0])) {
|
|
throw new RuntimeException("LockName: " + lockName +
|
|
" class name not matched. Expected: " + waitingLockName);
|
|
}
|
|
int i = Integer.parseInt(s[1], 16);
|
|
if (hcode != i) {
|
|
throw new RuntimeException("LockName: " + lockName +
|
|
" IdentityHashCode not matched. Expected: " + hcode);
|
|
}
|
|
}
|
|
|
|
void checkStackFrame(ThreadInfo info, StackTraceElement ste, int depth) {
|
|
StackTraceElement[] stacktrace = info.getStackTrace();
|
|
if (!ste.equals(stacktrace[depth])) {
|
|
System.out.println("LockedStackFrame:- " + ste);
|
|
System.out.println("StackTrace at " + depth + " :-" +
|
|
stacktrace[depth]);
|
|
throw new RuntimeException("LockedStackFrame does not match " +
|
|
"stack frame in ThreadInfo.getStackTrace");
|
|
}
|
|
}
|
|
void checkMonitor(MonitorInfo m, String methodName) {
|
|
for (Map.Entry<String, Lock[]> e : ownedMonitors.entrySet()) {
|
|
if (methodName.equals(e.getKey())) {
|
|
for (Lock l : e.getValue()) {
|
|
String className = l.getClass().getName();
|
|
int hcode = System.identityHashCode(l);
|
|
if (className.equals(m.getClassName()) &&
|
|
hcode == m.getIdentityHashCode()) {
|
|
// monitor matched the expected
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
throw new RuntimeException("Monitor not expected" + m);
|
|
}
|
|
void checkMonitor(String methodName, Lock l, MonitorInfo[] monitors) {
|
|
String className = l.getClass().getName();
|
|
int hcode = System.identityHashCode(l);
|
|
for (MonitorInfo m : monitors) {
|
|
if (className.equals(m.getClassName()) &&
|
|
hcode == m.getIdentityHashCode() &&
|
|
methodName.equals(m.getLockedStackFrame().getMethodName())) {
|
|
return;
|
|
}
|
|
}
|
|
throw new RuntimeException("Monitor not found in the returned list" +
|
|
" Method: " + methodName + " Lock: " + l);
|
|
|
|
}
|
|
|
|
static class Thread1 extends SynchronizerLockingThread {
|
|
public Thread1() {
|
|
super("t1");
|
|
initExpectedResult();
|
|
}
|
|
public void run() {
|
|
A();
|
|
}
|
|
void A() {
|
|
lock1.lock();
|
|
try {
|
|
lock2.lock();
|
|
try {
|
|
lock3.lock();
|
|
try {
|
|
B();
|
|
} finally {
|
|
lock3.unlock();
|
|
}
|
|
} finally {
|
|
lock2.unlock();
|
|
}
|
|
} finally {
|
|
lock1.unlock();
|
|
}
|
|
}
|
|
void B() {
|
|
lock4.lock();
|
|
try {
|
|
synchronized(lock5) {
|
|
C();
|
|
}
|
|
} finally {
|
|
lock4.unlock();
|
|
}
|
|
}
|
|
void C() {
|
|
synchronized(lock6) {
|
|
D();
|
|
}
|
|
}
|
|
void D() {
|
|
synchronized(lock7) {
|
|
try {
|
|
// signal to about to wait
|
|
count--;
|
|
lock7.wait();
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
Map<String, Lock[]> LOCKED_MONITORS;
|
|
Map<String, ReentrantLock[]> LOCKED_SYNCS;
|
|
Lock WAITING_LOCK = lock7;
|
|
int OWNED_MONITORS = 2;
|
|
int OWNED_SYNCS = 4;
|
|
void initExpectedResult() {
|
|
LOCKED_MONITORS = new HashMap<String, Lock[]>();
|
|
LOCKED_MONITORS.put("D", new Lock[0]); // no monitored locked
|
|
LOCKED_MONITORS.put("C", new Lock[] {lock6});
|
|
LOCKED_MONITORS.put("B", new Lock[] {lock5});
|
|
LOCKED_MONITORS.put("A", new Lock[0]);
|
|
|
|
LOCKED_SYNCS = new HashMap<String, ReentrantLock[]>();
|
|
LOCKED_SYNCS.put("D", new ReentrantLock[0]); // no sync locked
|
|
LOCKED_SYNCS.put("C", new ReentrantLock[0]); // no sync locked
|
|
LOCKED_SYNCS.put("B", new ReentrantLock[] {lock4});
|
|
LOCKED_SYNCS.put("A", new ReentrantLock[] {lock3, lock2, lock1});
|
|
this.setExpectedResult(WAITING_LOCK,
|
|
OWNED_MONITORS, LOCKED_MONITORS,
|
|
null,
|
|
OWNED_SYNCS, LOCKED_SYNCS);
|
|
}
|
|
|
|
}
|
|
|
|
static class Thread2 extends SynchronizerLockingThread {
|
|
Map<String, Lock[]> LOCKED_MONITORS = new HashMap<String, Lock[]>();
|
|
Map<String, ReentrantLock[]> LOCKED_SYNCS = new HashMap<String, ReentrantLock[]>();
|
|
Condition c = lock8.newCondition();
|
|
Condition WAITING_LOCK = c;
|
|
int OWNED_MONITORS = 0;
|
|
int OWNED_SYNCS = 0;
|
|
public Thread2() {
|
|
super("t2");
|
|
this.setExpectedResult(null,
|
|
OWNED_MONITORS, LOCKED_MONITORS,
|
|
WAITING_LOCK,
|
|
OWNED_SYNCS, LOCKED_SYNCS);
|
|
}
|
|
public void run() {
|
|
lock8.lock();
|
|
try {
|
|
synchronized(lock7) {
|
|
count--;
|
|
}
|
|
c.await();
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException(e);
|
|
} finally {
|
|
lock8.unlock();
|
|
}
|
|
throw new RuntimeException("should not reach here");
|
|
}
|
|
}
|
|
|
|
}
|