524 lines
18 KiB
Java
524 lines
18 KiB
Java
|
/*
|
||
|
* Copyright (c) 2006, 2018, 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.
|
||
|
*/
|
||
|
package nsk.share.jdi;
|
||
|
|
||
|
import java.util.*;
|
||
|
import nsk.share.TestBug;
|
||
|
import nsk.share.locks.MonitorLockingThread;
|
||
|
|
||
|
/*
|
||
|
* This class generates MonitorWaitEvent and MonitorWaitedEvent
|
||
|
*/
|
||
|
class MonitorWaitExecutor extends EventActionsExecutor {
|
||
|
// MonitorWaited event may occurs when waiting thread was interrupted,
|
||
|
// notified by notify or notifyAll, or when timeout expired
|
||
|
enum ExitFromWaitType {
|
||
|
EXIT_WITH_TIMEOUT,
|
||
|
INTERRUPT,
|
||
|
NOTIFY,
|
||
|
NOTIFY_ALL
|
||
|
}
|
||
|
|
||
|
// this thread forces MonitorWaitExecutor to exit from wait()
|
||
|
class AuxiliaryThread extends Thread {
|
||
|
private Thread threadToInterrupt;
|
||
|
|
||
|
private Object monitor;
|
||
|
|
||
|
public AuxiliaryThread(Thread threadToInterrupt, Object monitor) {
|
||
|
this.threadToInterrupt = threadToInterrupt;
|
||
|
this.monitor = monitor;
|
||
|
}
|
||
|
|
||
|
public void run() {
|
||
|
// wait when interrupted thread switches state to 'TIMED_WAITING'
|
||
|
while ((threadToInterrupt.getState() != Thread.State.WAITING) && !exitedFromWait) {
|
||
|
yield();
|
||
|
}
|
||
|
|
||
|
// threadToInterrupt 'spuriously' exited from wait()
|
||
|
if(exitedFromWait)
|
||
|
return;
|
||
|
|
||
|
if (exitFromWaitType == ExitFromWaitType.INTERRUPT) {
|
||
|
threadToInterrupt.interrupt();
|
||
|
} else if ((exitFromWaitType == ExitFromWaitType.NOTIFY)
|
||
|
|| (exitFromWaitType == ExitFromWaitType.NOTIFY_ALL)) {
|
||
|
/*
|
||
|
* NOTE: thread's state WAITING doesn't guarantee that thread released
|
||
|
* monitor, and entering to the next synchronized block may cause MonitorContentedEnterEvent
|
||
|
* (if corresponding request was created).
|
||
|
*
|
||
|
* Debugger should take in account this issue.
|
||
|
*/
|
||
|
synchronized (monitor) {
|
||
|
if (exitFromWaitType == ExitFromWaitType.NOTIFY)
|
||
|
monitor.notify();
|
||
|
else if (exitFromWaitType == ExitFromWaitType.NOTIFY_ALL)
|
||
|
monitor.notifyAll();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// thread may 'spuriously' exit from wait(), in this case AuxiliaryThread shouldn't wait 'WAITING' state
|
||
|
private volatile boolean exitedFromWait;
|
||
|
|
||
|
// save data about MonitorWait events
|
||
|
private boolean monitorWait;
|
||
|
|
||
|
// save data about MonitorWaited events
|
||
|
private boolean monitorWaited;
|
||
|
|
||
|
public MonitorWaitExecutor(boolean monitorWait, boolean monitorWaited) {
|
||
|
this.monitorWait = monitorWait;
|
||
|
this.monitorWaited = monitorWaited;
|
||
|
}
|
||
|
|
||
|
protected ExitFromWaitType exitFromWaitType;
|
||
|
|
||
|
public void doEventAction() {
|
||
|
for (ExitFromWaitType exitType : ExitFromWaitType.values()) {
|
||
|
exitFromWaitType = exitType;
|
||
|
|
||
|
monitorWait();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void monitorWait() {
|
||
|
|
||
|
exitedFromWait = false;
|
||
|
|
||
|
long timeout;
|
||
|
|
||
|
if (exitFromWaitType == ExitFromWaitType.EXIT_WITH_TIMEOUT)
|
||
|
timeout = 100;
|
||
|
else
|
||
|
timeout = 0;
|
||
|
|
||
|
if (monitorWait) {
|
||
|
DebuggeeEventData.DebugMonitorWaitEventData eventData = new DebuggeeEventData.DebugMonitorWaitEventData(
|
||
|
this, Thread.currentThread(), timeout, this);
|
||
|
addEventData(eventData);
|
||
|
}
|
||
|
|
||
|
if (monitorWaited) {
|
||
|
DebuggeeEventData.DebugMonitorWaitedEventData eventData = new DebuggeeEventData.DebugMonitorWaitedEventData(
|
||
|
this, Thread.currentThread(),
|
||
|
(exitFromWaitType == ExitFromWaitType.EXIT_WITH_TIMEOUT),
|
||
|
this);
|
||
|
addEventData(eventData);
|
||
|
}
|
||
|
|
||
|
AuxiliaryThread auxiliaryThread = null;
|
||
|
|
||
|
if (exitFromWaitType != ExitFromWaitType.EXIT_WITH_TIMEOUT) {
|
||
|
auxiliaryThread = new AuxiliaryThread(Thread.currentThread(), this);
|
||
|
auxiliaryThread.start();
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
eventMethod(timeout);
|
||
|
} catch (InterruptedException e) {
|
||
|
// expected exception
|
||
|
}
|
||
|
|
||
|
exitedFromWait = true;
|
||
|
|
||
|
if (auxiliaryThread != null) {
|
||
|
// don't using join, because of join is realized with using of
|
||
|
// wait(), and this method can generate unexpected events
|
||
|
while (auxiliaryThread.getState() != Thread.State.TERMINATED)
|
||
|
Thread.yield();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// move event actions to the separate method to simplify method redifinition
|
||
|
// in subclasses
|
||
|
synchronized protected void eventMethod(long timeout)
|
||
|
throws InterruptedException {
|
||
|
wait(timeout);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Subclass of MonitorWaitExecutor, define its own event method(copy of parent
|
||
|
* method), intended for event filters test
|
||
|
*/
|
||
|
class MonitorWaitExecutor_1Subclass extends MonitorWaitExecutor {
|
||
|
public MonitorWaitExecutor_1Subclass(boolean monitorWait,
|
||
|
boolean monitorWaited) {
|
||
|
super(monitorWait, monitorWaited);
|
||
|
}
|
||
|
|
||
|
synchronized protected void eventMethod(long timeout)
|
||
|
throws InterruptedException {
|
||
|
wait(timeout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Subclass of MonitorWaitExecutor, define its own event method(copy of parent
|
||
|
* method), intended for event filters test
|
||
|
*/
|
||
|
class MonitorWaitExecutor_2Subclass extends MonitorWaitExecutor_1Subclass {
|
||
|
public MonitorWaitExecutor_2Subclass(boolean monitorWait,
|
||
|
boolean monitorWaited) {
|
||
|
super(monitorWait, monitorWaited);
|
||
|
}
|
||
|
|
||
|
synchronized protected void eventMethod(long timeout)
|
||
|
throws InterruptedException {
|
||
|
wait(timeout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This class generates MonitorContendedEnterEvent and
|
||
|
* MonitorContendedEnteredEvent
|
||
|
*/
|
||
|
class MonitorEnterExecutor extends EventActionsExecutor {
|
||
|
/*
|
||
|
* Types of monitor acquiring:
|
||
|
* - through synchronized block
|
||
|
* - through synchronized method
|
||
|
* - through JNI MonitorEnter
|
||
|
*/
|
||
|
static enum MonitorAcquireType {
|
||
|
SYNCHRONIZED_BLOCK,
|
||
|
SYNCHRONIZED_METHOD,
|
||
|
JNI_MONITOR_ENTER
|
||
|
}
|
||
|
|
||
|
// native method uses this class
|
||
|
private static final Class<?> jniError = nsk.share.TestJNIError.class;
|
||
|
|
||
|
|
||
|
static final String nativeLibararyName = "MonitorEnterExecutor";
|
||
|
|
||
|
static {
|
||
|
System.loadLibrary(nativeLibararyName);
|
||
|
}
|
||
|
|
||
|
// this thread forces MonitorLockingThread to release holding lock
|
||
|
static class LockFreeThread extends Thread {
|
||
|
private Thread blockedThread;
|
||
|
|
||
|
private MonitorLockingThread lockingThread;
|
||
|
|
||
|
public LockFreeThread(Thread blockedThread,
|
||
|
MonitorLockingThread lockingThread) {
|
||
|
this.blockedThread = blockedThread;
|
||
|
this.lockingThread = lockingThread;
|
||
|
}
|
||
|
|
||
|
public void run() {
|
||
|
// wait when blocked thread switches state to 'BLOCKED'
|
||
|
while (blockedThread.getState() != Thread.State.BLOCKED)
|
||
|
yield();
|
||
|
|
||
|
lockingThread.releaseLock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private boolean monitorEnter;
|
||
|
|
||
|
private boolean monitorEntered;
|
||
|
|
||
|
public MonitorEnterExecutor(boolean monitorEnter, boolean monitorEntered) {
|
||
|
this.monitorEnter = monitorEnter;
|
||
|
this.monitorEntered = monitorEntered;
|
||
|
}
|
||
|
|
||
|
protected void monitorEnter() {
|
||
|
// locking thread acquires 'this' lock
|
||
|
MonitorLockingThread lockingThread = new MonitorLockingThread(this);
|
||
|
lockingThread.acquireLock();
|
||
|
|
||
|
if (monitorEnter) {
|
||
|
DebuggeeEventData.DebugMonitorEnterEventData eventData = new DebuggeeEventData.DebugMonitorEnterEventData(
|
||
|
this, Thread.currentThread(), this);
|
||
|
addEventData(eventData);
|
||
|
}
|
||
|
if (monitorEntered) {
|
||
|
DebuggeeEventData.DebugMonitorEnteredEventData eventData = new DebuggeeEventData.DebugMonitorEnteredEventData(
|
||
|
this, Thread.currentThread(), this);
|
||
|
addEventData(eventData);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This thread forces lockingThread to free 'this' lock when current thread's state will change to 'BLOCKED'
|
||
|
*
|
||
|
* NOTE: this method to provoke MonitorContended events isn't 100% robust
|
||
|
* Tests should take in account this issue.
|
||
|
*
|
||
|
*/
|
||
|
LockFreeThread lockFreeThread = new LockFreeThread(Thread.currentThread(), lockingThread);
|
||
|
lockFreeThread.start();
|
||
|
|
||
|
// try to acquire 'this' lock
|
||
|
eventMethod();
|
||
|
|
||
|
while(lockingThread.getState() != Thread.State.TERMINATED)
|
||
|
Thread.yield();
|
||
|
|
||
|
while(lockFreeThread.getState() != Thread.State.TERMINATED)
|
||
|
Thread.yield();
|
||
|
}
|
||
|
|
||
|
private MonitorAcquireType monitorAcquireType;
|
||
|
|
||
|
// generate events in different ways
|
||
|
public void doEventAction() {
|
||
|
for (MonitorAcquireType monitorAcquireType : MonitorAcquireType.values()) {
|
||
|
this.monitorAcquireType = monitorAcquireType;
|
||
|
monitorEnter();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void eventMethod() {
|
||
|
switch (monitorAcquireType) {
|
||
|
case SYNCHRONIZED_BLOCK:
|
||
|
synchronizedBlock();
|
||
|
break;
|
||
|
case SYNCHRONIZED_METHOD:
|
||
|
synchronizedMethod();
|
||
|
break;
|
||
|
case JNI_MONITOR_ENTER:
|
||
|
nativeJNIMonitorEnter();
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
throw new TestBug("Invalid monitorAcquireType: "
|
||
|
+ monitorAcquireType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// move event actions to the separate methods to simplify method
|
||
|
// redifinition in subclasses:
|
||
|
|
||
|
protected void synchronizedBlock() {
|
||
|
// MonitorContendedEnterEvent and MonitorContendedEnteredEvent should occur here
|
||
|
synchronized (this) {
|
||
|
// don't expect events for following synchronized block
|
||
|
synchronized (this) {
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MonitorContendedEnterEvent and MonitorContendedEnteredEvent should occur here
|
||
|
synchronized protected void synchronizedMethod() {
|
||
|
// don't expect events for following synchronized block
|
||
|
synchronized (this) {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// call JNI methods MonitorEnter and MonitorExit for 'this' object
|
||
|
protected native void nativeJNIMonitorEnter();
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Subclass of MonitorEnterExecutor, defines its own event methods(copy of parent
|
||
|
* method), intended for event filters test
|
||
|
*/
|
||
|
class MonitorEnterExecutor_1Subclass extends MonitorEnterExecutor {
|
||
|
static {
|
||
|
System.loadLibrary(nativeLibararyName);
|
||
|
}
|
||
|
|
||
|
public MonitorEnterExecutor_1Subclass(boolean monitorEnter,
|
||
|
boolean monitorEntered) {
|
||
|
super(monitorEnter, monitorEntered);
|
||
|
}
|
||
|
|
||
|
protected void synchronizedBlock() {
|
||
|
// MonitorContendedEnterEvent and MonitorContendedEnteredEvent should occur here
|
||
|
synchronized (this) {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MonitorContendedEnterEvent and MonitorContendedEnteredEvent should occur here
|
||
|
synchronized protected void synchronizedMethod() {
|
||
|
}
|
||
|
|
||
|
// call JNI methods MonitorEnter and MonitorExit for 'this' object
|
||
|
protected native void nativeJNIMonitorEnter();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Subclass of MonitorEnterExecutor, defines its own event methods(copy of parent
|
||
|
* method), intended for event filters test
|
||
|
*/
|
||
|
class MonitorEnterExecutor_2Subclass extends MonitorEnterExecutor_1Subclass {
|
||
|
static {
|
||
|
System.loadLibrary(nativeLibararyName);
|
||
|
}
|
||
|
|
||
|
public MonitorEnterExecutor_2Subclass(boolean monitorEnter,
|
||
|
boolean monitorEntered) {
|
||
|
super(monitorEnter, monitorEntered);
|
||
|
}
|
||
|
|
||
|
protected void synchronizedBlock() {
|
||
|
// MonitorContendedEnterEvent and MonitorContendedEnteredEvent should occur here
|
||
|
synchronized (this) {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MonitorContendedEnterEvent and MonitorContendedEnteredEvent should occur here
|
||
|
synchronized protected void synchronizedMethod() {
|
||
|
}
|
||
|
|
||
|
// call JNI methods MonitorEnter and MonitorExit for 'this' object
|
||
|
protected native void nativeJNIMonitorEnter();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Class is used as base debuggee in tests for following events and event requests:
|
||
|
* - MonitorContendedEnterRequest / MonitorContendedEnterEvent
|
||
|
* - MonitorContendedEnteredRequest / MonitorContendedEnteredEvent
|
||
|
* - MonitorWaitRequest / MonitorWaitEvent
|
||
|
* - MonitorWaitedRequest / MonitorWaitedEvent
|
||
|
*/
|
||
|
public class MonitorEventsDebuggee extends JDIEventsDebuggee {
|
||
|
public static void main(String[] args) {
|
||
|
MonitorEventsDebuggee debuggee = new MonitorEventsDebuggee();
|
||
|
debuggee.doTest(args);
|
||
|
}
|
||
|
|
||
|
protected void createActionsExecutors(String description, int eventsCount) {
|
||
|
boolean monitorEnter = description
|
||
|
.contains(JDIEventsDebugger.EventType.MONITOR_CONTENTED_ENTER
|
||
|
.name());
|
||
|
boolean monitorEntered = description
|
||
|
.contains(JDIEventsDebugger.EventType.MONITOR_CONTENTED_ENTERED
|
||
|
.name());
|
||
|
boolean monitorWait = description
|
||
|
.contains(JDIEventsDebugger.EventType.MONITOR_WAIT.name());
|
||
|
boolean monitorWaited = description
|
||
|
.contains(JDIEventsDebugger.EventType.MONITOR_WAITED.name());
|
||
|
|
||
|
if (monitorEnter || monitorEntered || monitorWait || monitorWaited) {
|
||
|
createActionsExecutors(monitorEnter, monitorEntered, monitorWait,
|
||
|
monitorWaited, eventsCount);
|
||
|
} else
|
||
|
throw new TestBug(
|
||
|
"Invalid command format (required event not specified)");
|
||
|
}
|
||
|
|
||
|
private List<DebuggeeEventData.DebugMonitorEventData> eventsData = new ArrayList<DebuggeeEventData.DebugMonitorEventData>();
|
||
|
|
||
|
protected void clearResults() {
|
||
|
super.clearResults();
|
||
|
eventsData.clear();
|
||
|
}
|
||
|
|
||
|
private void createActionsExecutors(boolean monitorEnter,
|
||
|
boolean monitorEntered, boolean monitorWait, boolean monitorWaited,
|
||
|
int actionsCount) {
|
||
|
EventActionsThread thread;
|
||
|
|
||
|
// create 3 instances of generating objects of 3 different classes (for
|
||
|
// event filters tests)
|
||
|
if (monitorEnter || monitorEntered) {
|
||
|
thread = new EventActionsThread(new MonitorEnterExecutor(
|
||
|
monitorEnter, monitorEntered), actionsCount);
|
||
|
thread.start();
|
||
|
eventActionsExecutorsPool.add(thread);
|
||
|
|
||
|
thread = new EventActionsThread(new MonitorEnterExecutor_1Subclass(
|
||
|
monitorEnter, monitorEntered), actionsCount);
|
||
|
thread.start();
|
||
|
eventActionsExecutorsPool.add(thread);
|
||
|
|
||
|
thread = new EventActionsThread(new MonitorEnterExecutor_2Subclass(
|
||
|
monitorEnter, monitorEntered), actionsCount);
|
||
|
thread.start();
|
||
|
eventActionsExecutorsPool.add(thread);
|
||
|
}
|
||
|
|
||
|
// create 3 instances of generating objects of 3 different classes (for
|
||
|
// event filters tests)
|
||
|
if (monitorWait || monitorWaited) {
|
||
|
thread = new EventActionsThread(new MonitorWaitExecutor(
|
||
|
monitorWait, monitorWaited), actionsCount);
|
||
|
thread.start();
|
||
|
eventActionsExecutorsPool.add(thread);
|
||
|
|
||
|
thread = new EventActionsThread(new MonitorWaitExecutor_1Subclass(
|
||
|
monitorWait, monitorWaited), actionsCount);
|
||
|
thread.start();
|
||
|
eventActionsExecutorsPool.add(thread);
|
||
|
|
||
|
thread = new EventActionsThread(new MonitorWaitExecutor_2Subclass(
|
||
|
monitorWait, monitorWaited), actionsCount);
|
||
|
thread.start();
|
||
|
eventActionsExecutorsPool.add(thread);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// start event generating threads and wait when all this threads finish
|
||
|
// execution
|
||
|
// override parent method because of Thread.join() used in parent method
|
||
|
// generates unexpected MonitorWait/MonitorWaited events
|
||
|
protected void startExecution() {
|
||
|
if (eventActionsExecutorsPool.size() == 0) {
|
||
|
throw new TestBug("ActionsExecutors were not created");
|
||
|
}
|
||
|
|
||
|
for (EventActionsThread thread : eventActionsExecutorsPool) {
|
||
|
thread.startExecution();
|
||
|
}
|
||
|
|
||
|
// wait completion of test threads in separate thread to free thread listening commands
|
||
|
executionControllingThread = new Thread(
|
||
|
new Runnable() {
|
||
|
public void run() {
|
||
|
boolean executionCompleted;
|
||
|
do {
|
||
|
try {
|
||
|
// give time to event generators
|
||
|
Thread.sleep(500);
|
||
|
} catch (InterruptedException e) {
|
||
|
unexpectedException(e);
|
||
|
}
|
||
|
executionCompleted = true;
|
||
|
for (EventActionsThread thread : eventActionsExecutorsPool) {
|
||
|
if (thread.isAlive()) {
|
||
|
executionCompleted = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} while (!executionCompleted);
|
||
|
|
||
|
completeExecution();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
executionControllingThread.start();
|
||
|
}
|
||
|
|
||
|
}
|