blob: cffa8150664d9c48b5521e61c04fba6039171be2 [file] [log] [blame]
package org.eclipse.jdt.internal.debug.ui.monitors;
/**********************************************************************
Copyright (c) 2000, 2002 IBM Corp. All rights reserved.
This file is made available under the terms of the Common Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/cpl-v10.html
**********************************************************************/
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
/**
* Handles all the data for the Threads and Monitors view.
*/
public class MonitorManager {
/**
* Table containing the references to the monitors owned by the threads
*/
private Map fThreadToOwnedMonitors;
/**
* Table containing the references to the monitor contended by the threads
*/
private Map fThreadToContendedMonitor;
/**
* Table containing the references to the thread owning the monitors
*/
private Map fMonitorToOwningThread;
/**
* Table containing the references to the threads contending the monitors
*/
private Map fMonitorToContendingThreads;
private static MonitorManager fgDefault= null;
private DeadLocksViewContentProvider fDeadlockUpdateListener;
/**
* List containing the lists of the different deadlocks
*/
private List fDeadLockLists;
/**
* Constructor
*/
private MonitorManager() {
//@see getDefault()
fThreadToOwnedMonitors= new Hashtable(4);
fThreadToContendedMonitor= new Hashtable(4);
fMonitorToOwningThread= new Hashtable();
fMonitorToContendingThreads= new Hashtable();
fDeadLockLists = new ArrayList();
}
public static MonitorManager getDefault() {
if (fgDefault == null) {
fgDefault= new MonitorManager();
}
return fgDefault;
}
/**
* Adds the the monitors owned by the thread
* If the list is <code>null</code>, remove the thread from the mappings
* @param thread The thread
* @param monitors The monitors owned by the thread
*/
protected void addThreadWithOwnedMonitors(IJavaThread thread, IJavaObject[] monitors) {
if (monitors == null) {
fThreadToOwnedMonitors.remove(thread);
} else {
fThreadToOwnedMonitors.put(thread, monitors);
}
}
/**
* Adds the monitor contended by the thread
* If the list is <code>null</code>, remove the thread from the mappings
* @param thread The thread
* @param monitor The monitor contended by the thread
*/
protected void addThreadWithContendedMonitor(IJavaThread thread, IJavaObject monitor) {
if (monitor == null) {
fThreadToContendedMonitor.remove(thread);
} else {
fThreadToContendedMonitor.put(thread, monitor);
}
}
/**
* Adds the thread owning the monitor
* If the list is <code>null</code>, remove the monitor from the mappings
* @param monitor The monitor
* @param thread The thread owning the monitor
*/
protected void addMonitorWithOwningThread(IJavaObject monitor, IJavaThread thread) {
if (monitor == null) {
fMonitorToOwningThread.remove(monitor);
} else {
fMonitorToOwningThread.put(monitor, thread);
}
}
/**
* Adds a thread waiting for the monitor
* If the list is <code>null</code>, remove the monitors from the mappings
* @param monitor The monitor
* @param thread The thread waiting for the monitor
*/
protected void addMonitorWithContendedThread(IJavaObject monitor, IJavaThread thread) {
if (monitor == null) {
fMonitorToContendingThreads.remove(monitor);
} else {
List threads= (List)fMonitorToContendingThreads.get(monitor);
if (threads == null) {
threads= new ArrayList();
fMonitorToContendingThreads.put(monitor, threads);
}
threads.add(thread);
}
}
/**
* Returns the monitors owned by the given thread, or <code>null</code>
* if the thread does not own any monitors.
*
* @param thread The thread owning the monitors
* @return The monitors owned by the given thread
*/
public IJavaObject[] getOwnedMonitors(IJavaThread thread) {
return (IJavaObject[])fThreadToOwnedMonitors.get(thread);
}
/**
* Returns the monitor contended by the given thread, or <code>null</code>
*
* @param thread The thread from to determine the contended monitor
* @return The monitor contended by the given thread
*/
public IJavaObject getContendedMonitor(IJavaThread thread) {
return (IJavaObject)fThreadToContendedMonitor.get(thread);
}
/**
* Returns the thread owning the given monitor, or <code>null</code>
* if no thread owns the specified monitor.
*
* @param monitor The monitor from to determine the owning thread
* @return The thread owning the given monitor
*/
public IJavaThread getOwningThread(IJavaObject monitor) {
return (IJavaThread)fMonitorToOwningThread.get(monitor);
}
/**
* Returns the list of threads awaiting the given monitor, or <code>null</code>
*
* @param monitor The monitor from to determine the contending threads
* @return List a list of the threads in contention for the monitor
*/
public List getContendingThreads(IJavaObject monitor) {
Object obj = fMonitorToContendingThreads.get(monitor);
return (List)obj;
}
/**
* Returns all the threads owning or waiting on a monitor
*
* @return All the threads (owning or waiting on a monitor)
*/
public IJavaThread[] getThreads() {
Set all= new HashSet();
all.addAll(fThreadToContendedMonitor.keySet());
all.addAll(fThreadToOwnedMonitors.keySet());
return (IJavaThread[])all.toArray(new IJavaThread[all.size()]);
}
/**
* Returns all the monitors owned or in contention.
*
* @return All the monitors (owned or in contention)
*/
public IJavaObject[] getMonitors() {
Set all= new HashSet();
all.addAll(fMonitorToContendingThreads.keySet());
all.addAll(fMonitorToOwningThread.keySet());
return (IJavaObject[])all.toArray(new IJavaObject[all.size()]);
}
/**
* Updates the data on threads, monitors and deadlocks
* for the specified debug target.
*
* @param target The debug target
*/
public void update(IJavaDebugTarget target){
removeMonitorInformation(target);
if (!target.supportsMonitorInformation()) {
return;
}
if (fDeadlockUpdateListener != null) {
fDeadlockUpdateListener.clearDeadlockInformation();
}
update(target, true);
}
/**
* Updates the data on threads, monitors and deadlocks
* for the suspended threads contained within the specified
* debug target.
*
* @param target The debug target
* @see update(IJavaDebugTarget target)
*/
public void updatePartial(IJavaDebugTarget target){
removeMonitorInformation(target);
if (!target.supportsMonitorInformation()) {
return;
}
if (fDeadlockUpdateListener != null) {
fDeadlockUpdateListener.clearDeadlockInformation();
}
update(target, false);
}
/**
* Updates the data on threads, monitors and deadlocks
* for the suspended threads contained within the specified
* debug target. If <code>suspendThreads</code>,
*
* @param target The debug target
* @param whether to suspend the threads
*/
private void update(IJavaDebugTarget target, boolean suspendThreads){
try {
// construct the list of all the non system threads
IThread[] threadResult= target.getThreads();
List threadsList = new ArrayList(threadResult.length);
IJavaThread thread;
for (int i = 0; i < threadResult.length; i++) {
thread = (IJavaThread)threadResult[i];
if(!thread.isSystemThread()){
threadsList.add(thread);
}
}
IJavaThread[] threads= (IJavaThread[]) threadsList.toArray(new IJavaThread[threadsList.size()]);
if (suspendThreads) {
//suspend all the non system threads
suspend(threads);
}
//updating data on owning threads / owned monitors
// and contending threads / contended monitors
for (int i = 0; i < threads.length; i++) {
thread = threads[i];
updateMonitors(thread);
}
//all of the monitor information is needed before
//the deadlock information can be calculated
for (int i = 0; i < threads.length; i++) {
thread = threads[i];
updateDeadlock(thread);
}
} catch(DebugException e){
JDIDebugUIPlugin.log(e);
}
}
protected void updateDeadlock(IJavaThread thread) throws DebugException {
//updating data on deadlocks
List l = listToDeadlock(thread, new ArrayList(4));
// if thread is caught in a deadlock,
// l will be the list showing this deadlock
if(l != null){
ThreadWrapper tw = new ThreadWrapper(thread, l);
// adding this deadlock list
fDeadLockLists.add(tw);
}
}
protected void updateMonitors(IJavaThread thread) throws DebugException {
IJavaObject[] ownedMonitors;
IJavaObject currentContendedMonitor;
IJavaObject monitor;
ownedMonitors = thread.getOwnedMonitors();
currentContendedMonitor = thread.getContendedMonitor();
// owning threads / owned monitors
if(thread.hasOwnedMonitors()){
addThreadWithOwnedMonitors(thread, ownedMonitors);
for(int j=0; j < ownedMonitors.length; j++) {
monitor = ownedMonitors[j];
addMonitorWithOwningThread(monitor, thread);
}
}
// contending threads / contended monitors
if(currentContendedMonitor != null){
addThreadWithContendedMonitor(thread, currentContendedMonitor);
addMonitorWithContendedThread(currentContendedMonitor, thread);
}
}
/**
* Suspend all the given threads
* @param The list of threads to suspend
*/
private void suspend(IJavaThread[] threads){
try {
for (int i = 0; i < threads.length; i++) {
IJavaThread thread = threads[i];
if (!thread.isSuspended()) {
thread.suspend();
while (!thread.isSuspended()) {
Thread.sleep(100);
}
}
}
}
catch (DebugException e) {
JDIDebugUIPlugin.log(e);
}
catch (InterruptedException e){
JDIDebugUIPlugin.log(e);
}
}
/**
* Clears all the cached monitor information for the specified target.
*
* @param target The target to remove the cached information for
*/
public void removeMonitorInformation(IJavaDebugTarget target) {
fThreadToOwnedMonitors.clear();
fThreadToContendedMonitor.clear();
fMonitorToOwningThread.clear();
fMonitorToContendingThreads.clear();
fDeadLockLists.clear();
if (fDeadlockUpdateListener != null) {
fDeadlockUpdateListener.clearDeadlockInformation();
}
}
/**
* If the thread is in a deadlock, returns the list to the deadlock
* This list has the following structure:
* <ul>
* <li>First element: Thread in the deadlock or waiting on a monitor that is involved in a deadlock</li>
* <li>Second element: Monitor contended by the first element</li>
* <li>Third element: Thread owning the second element</li>
* <li>Fourth element: Monitor contended by the third element</li>
* <li>...</li>
* <li>Last element: Same element as the first one, proving that it is in a deadlock</li>
* </ul>
*
* @param thread The thread we want to get the list of
* @param threadTree The list that records the element already used (call with an empty list)
* @return The deadlock list
*/
private List listToDeadlock(IJavaThread thread, List usedThreadsList){
List res = new ArrayList();
IJavaObject contendedMonitor = (IJavaObject)fThreadToContendedMonitor.get(thread);
//if the thread is waiting for one monitor
if(contendedMonitor!=null){
IJavaThread owningThread = (IJavaThread)fMonitorToOwningThread.get(contendedMonitor);
// check if owningThread has already been used, and therefore is already in the given list
// if owningThread has already been used, returns the end of the list
if(usedThreadsList.contains(owningThread)){
res.add(thread);
res.add(contendedMonitor);
res.add(owningThread);
return res;
}
// if owningThread has not already been used
else{
List newUsedThreadsList= new ArrayList(usedThreadsList);
//adding current thread to the new used list
newUsedThreadsList.add(thread);
if(owningThread==null){
return null;
}
// recursive call, one level lower in the deadlock list
List newRes = listToDeadlock(owningThread, newUsedThreadsList);
if(newRes!=null){
res.add(thread);
res.add(contendedMonitor);
res.addAll(newRes);
return res;
}
}
} else {
// if the thread is not waiting for any monitor
return null;
}
return null;
}
/**
* Returns the number of determined deadlocks
*
* @return List a list of all of the listings of current deadlocks
*/
public int getNumberOfDeadlocks() {
return fDeadLockLists.size();
}
/**
* Returns the deadlock list at the specified index or <code>null</code>
* if the index is greater than the number of detected deadlocks.
*
* @return List a list of all of the listings of current deadlocks
* @see getNumberOfDeadlocks();
*/
public List getDeadlockList(int index) {
if (index >= fDeadLockLists.size()) {
return null;
}
return ((ThreadWrapper)fDeadLockLists.get(index)).getDeadLockList();
}
/**
* Returns the thread that is at the root of the deadlock at the specified
* index or <code>null</code> if the index is greater than the number of
* detected deadlocks.
*
* @return IJavaThread the thread at the root of the deadlock
* @see getNumberOfDeadlocks();
*/
public IJavaThread getStartThread(int index) {
if (index >= fDeadLockLists.size()) {
return null;
}
return ((ThreadWrapper)fDeadLockLists.get(index)).getStartThread();
}
/**
* Returns whether the given thread is caught in a deadlock
*
* @param thread The thread to check if in deadlock
* @return <code>true<code> if the thread is in a deadlock, <code>false<code> otherwise.
*/
public boolean isCaughtInDeadlock(IJavaThread thread){
for (int i = 0; i < fDeadLockLists.size(); i++) {
if(((ThreadWrapper)fDeadLockLists.get(i)).getStartThread().equals(thread)){
return true;
}
}
return false;
}
protected void addDeadlockUpdateListener(DeadLocksViewContentProvider provider) {
fDeadlockUpdateListener= provider;
}
protected void removeDeadlockUpdateListener() {
fDeadlockUpdateListener= null;
}
}