| /******************************************************************************* |
| * Copyright (c) 2004, 2012 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.debug.ui.monitors; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.PlatformObject; |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.model.IDebugElement; |
| import org.eclipse.debug.core.model.IDebugTarget; |
| import org.eclipse.debug.core.model.IThread; |
| import org.eclipse.jdt.debug.core.IJavaObject; |
| import org.eclipse.jdt.debug.core.IJavaThread; |
| |
| import com.sun.jdi.IncompatibleThreadStateException; |
| |
| /** |
| * Represent a Java thread in the threads and monitors model. |
| */ |
| public class JavaMonitorThread extends PlatformObject { |
| |
| /** |
| * The underlying thread. |
| */ |
| private IJavaThread fThread; |
| |
| private IThread fOriginalThread; |
| |
| /** |
| * The monitor this thread is waiting for. |
| */ |
| private JavaMonitor fContendedMonitor; |
| /** |
| * The monitors owned by this thread. |
| */ |
| private JavaMonitor[] fOwnedMonitors= new JavaMonitor[0]; |
| /** |
| * Indicate if this thread is currently part of a deadlock. |
| */ |
| private boolean fIsInDeadlock; |
| /** |
| * Indicate that the information for this thread need to be update, it |
| * may have changed. |
| */ |
| private boolean fToUpdate= true; |
| |
| /** |
| * List of JavaOwningThread and JavaWaitingThread associated with this thread. |
| */ |
| private List<IDebugElement> fElements= new ArrayList<IDebugElement>(); |
| |
| /** |
| * JavaWaitingThread object used to return the JavaOwnedMonitor for this |
| * thread. |
| */ |
| private JavaWaitingThread fBaseWaitingThread; |
| /** |
| * JavaOwningThread object uset to return the JavaWaitingMonitor for this |
| * thread. |
| */ |
| private JavaOwningThread fBaseOwningThread; |
| |
| public JavaMonitorThread(IJavaThread underlyingThread, IThread originalThread) { |
| fThread= underlyingThread; |
| fOriginalThread= originalThread; |
| } |
| |
| public IJavaThread getThread() { |
| return fThread; |
| } |
| |
| public IThread getOriginalThread() { |
| return fOriginalThread; |
| } |
| |
| protected void setOriginalThread(IThread originalThread) { |
| fOriginalThread= originalThread; |
| } |
| |
| /** |
| * @see org.eclipse.debug.core.model.IDebugElement#getModelIdentifier() |
| */ |
| public String getModelIdentifier() { |
| return fThread.getModelIdentifier(); |
| } |
| |
| /** |
| * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget() |
| */ |
| public IDebugTarget getDebugTarget() { |
| return fThread.getDebugTarget(); |
| } |
| |
| /** |
| * @see org.eclipse.debug.core.model.IDebugElement#getLaunch() |
| */ |
| public ILaunch getLaunch() { |
| return fThread.getLaunch(); |
| } |
| |
| /** |
| * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended() |
| */ |
| public boolean isSuspended() { |
| return fThread.isSuspended(); |
| } |
| |
| /** |
| * Returns the contended monitor to be used as a child |
| * of the underlying thread in the debug launch view. |
| */ |
| public JavaContendedMonitor getContendedMonitor() { |
| if (fBaseOwningThread == null) { |
| fBaseOwningThread= new JavaOwningThread(this, null); |
| } |
| return fBaseOwningThread.getContendedMonitor(); |
| } |
| |
| /** |
| * Returns the owned monitors to be used as children |
| * of the underlying thread in the debug launch view. |
| */ |
| public JavaOwnedMonitor[] getOwnedMonitors() { |
| if (fBaseWaitingThread == null) { |
| fBaseWaitingThread= new JavaWaitingThread(this, null); |
| } |
| return fBaseWaitingThread.getOwnedMonitors(); |
| } |
| |
| /** |
| * Returns the monitor this thread is waiting for. |
| */ |
| protected JavaMonitor getContendedMonitor0() { |
| if (fToUpdate) { |
| update(); |
| } |
| return fContendedMonitor; |
| } |
| |
| /** |
| * Returns the monitors owned by this thread. |
| */ |
| protected JavaMonitor[] getOwnedMonitors0() { |
| if (fToUpdate) { |
| update(); |
| } |
| return fOwnedMonitors; |
| } |
| |
| /** |
| * Update the information for this thread. |
| * @return <code>true</code> if the contended monitor or |
| * the owned monitors changed. |
| */ |
| private boolean update() { |
| boolean changed= false; |
| synchronized(this) { |
| if (!fToUpdate) { |
| return false; |
| } |
| try { |
| // update the contended monitor |
| IJavaObject contendedMonitor= fThread.getContendedMonitor(); |
| if (contendedMonitor == null) { |
| changed= fContendedMonitor != null; |
| fContendedMonitor= null; |
| } else { |
| changed= fContendedMonitor == null || !contendedMonitor.equals(fContendedMonitor.getMonitor()); |
| fContendedMonitor= ThreadMonitorManager.getDefault().getJavaMonitor(contendedMonitor); |
| } |
| // update the owned monitors |
| IJavaObject[] ownedMonitors= fThread.getOwnedMonitors(); |
| if (ownedMonitors == null || ownedMonitors.length == 0) { |
| // no owned monitor, not much to do |
| changed= fOwnedMonitors != null && fOwnedMonitors.length != 0; |
| fOwnedMonitors= new JavaMonitor[0]; |
| } else { |
| JavaMonitor[] tmp= new JavaMonitor[ownedMonitors.length]; |
| ThreadMonitorManager threadMonitorManager= ThreadMonitorManager.getDefault(); |
| if (changed || fOwnedMonitors.length != ownedMonitors.length) { |
| // if we know it changed, we can just create the new list. |
| for (int i= 0; i < ownedMonitors.length; i++) { |
| tmp[i]= threadMonitorManager.getJavaMonitor(ownedMonitors[i]); |
| } |
| changed= true; |
| } else { |
| // we need to check in the new list contains the same monitors as the |
| // previous list |
| int sameMonitor= 0; |
| for (int i= 0; i < ownedMonitors.length; i++) { |
| for (int j= 0; j < fOwnedMonitors.length; j++) { |
| if (ownedMonitors[i].equals(fOwnedMonitors[i].getMonitor())) { |
| sameMonitor++; |
| break; |
| } |
| } |
| tmp[i]= threadMonitorManager.getJavaMonitor(ownedMonitors[i]); |
| } |
| changed= sameMonitor != ownedMonitors.length; |
| } |
| fOwnedMonitors= tmp; |
| } |
| } catch (DebugException e) { |
| Throwable cause= e.getStatus().getException(); |
| if (!(cause instanceof IncompatibleThreadStateException)) { |
| // IncompatibleThreadStateException are expected from Sun VMs |
| // if the thread is not suspended. |
| // For all other exceptions, null the values. |
| fContendedMonitor= null; |
| changed= fOwnedMonitors != null && fOwnedMonitors.length != 0; |
| fOwnedMonitors= new JavaMonitor[0]; |
| } |
| } finally { |
| fToUpdate= false; |
| } |
| } |
| if (changed) { |
| fireChangeEvent(DebugEvent.CONTENT); |
| } |
| return changed; |
| } |
| |
| /** |
| * send a change event for theJavaWaitingThread and JavaOwningThread |
| * associated with this thread |
| */ |
| private void fireChangeEvent(int detail) { |
| Object[] elements= fElements.toArray(); |
| List<Object> changedElement= new ArrayList<Object>(); |
| if (fOriginalThread != null) { |
| changedElement.add(fOriginalThread); |
| } |
| for (int i= 0; i < elements.length; i++) { |
| Object element= elements[i]; |
| // the two 'base' elements are not part of the hierarchy, they are |
| // used to get the children of the Thread. |
| if (element != fBaseOwningThread && element != fBaseWaitingThread) { |
| changedElement.add(element); |
| } |
| } |
| DebugEvent[] changeEvents= new DebugEvent[changedElement.size()]; |
| int i= 0; |
| for (Iterator<Object> iter= changedElement.iterator(); iter.hasNext();) { |
| changeEvents[i++]= new DebugEvent(iter.next(), DebugEvent.CHANGE, detail); |
| } |
| DebugPlugin.getDefault().fireDebugEventSet(changeEvents); |
| } |
| |
| public synchronized void setToUpdate() { |
| if (!fToUpdate) { |
| fToUpdate= true; |
| if (fContendedMonitor != null) { |
| fContendedMonitor.setToUpdate(); |
| } |
| if (fOwnedMonitors != null) { |
| for (int i= 0; i < fOwnedMonitors.length; i++) { |
| fOwnedMonitors[i].setToUpdate(); |
| } |
| } |
| } |
| } |
| |
| protected void addElement(JavaOwningThread thread) { |
| fElements.add(thread); |
| } |
| |
| protected void addElement(JavaWaitingThread thread) { |
| fElements.add(thread); |
| } |
| |
| public void refresh() { |
| if (fToUpdate && !update()) { |
| if (fContendedMonitor != null) { |
| fContendedMonitor.refresh(); |
| } |
| for (int i= 0; i < fOwnedMonitors.length; i++) { |
| fOwnedMonitors[i].refresh(); |
| } |
| } |
| } |
| |
| /** |
| * Indicate if this thread is currently part of a deadlock |
| */ |
| public boolean isInDeadlock() { |
| return fIsInDeadlock; |
| } |
| /** |
| * Set this thread as being part of a deadlock. |
| */ |
| public void setInDeadlock(boolean isInDeadlock) { |
| boolean oldValue= fIsInDeadlock; |
| fIsInDeadlock = isInDeadlock; |
| if (oldValue != isInDeadlock) { |
| fireChangeEvent(DebugEvent.STATE); |
| } |
| } |
| } |