mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-05 08:06:56 +07:00
289 lines
9.7 KiB
Java
289 lines
9.7 KiB
Java
package com.intellij.debugger.engine;
|
|
|
|
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
|
|
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
|
|
import com.intellij.openapi.diagnostic.Logger;
|
|
import com.intellij.Patches;
|
|
import com.sun.jdi.event.EventSet;
|
|
import com.sun.jdi.request.EventRequest;
|
|
import com.sun.jdi.InternalException;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Created by IntelliJ IDEA.
|
|
* User: lex
|
|
* Date: Feb 24, 2004
|
|
* Time: 8:04:01 PM
|
|
* To change this template use File | Settings | File Templates.
|
|
*/
|
|
public class SuspendManagerImpl implements SuspendManager {
|
|
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.SuspendManager");
|
|
|
|
private final LinkedList<SuspendContextImpl> myEventContexts = new LinkedList<SuspendContextImpl>();
|
|
private final LinkedList<SuspendContextImpl> myPausedContexts = new LinkedList<SuspendContextImpl>();
|
|
private final Set<ThreadReferenceProxyImpl> myFrozenThreads = new HashSet<ThreadReferenceProxyImpl>();
|
|
|
|
private final DebugProcessImpl myDebugProcess;
|
|
|
|
public int suspends = 0;
|
|
|
|
public SuspendManagerImpl(DebugProcessImpl debugProcess) {
|
|
myDebugProcess = debugProcess;
|
|
myDebugProcess.addDebugProcessListener(new DebugProcessAdapterImpl() {
|
|
public void processDetached(DebugProcessImpl process) {
|
|
myEventContexts.clear();
|
|
myPausedContexts.clear();
|
|
myFrozenThreads.clear();
|
|
}
|
|
});
|
|
}
|
|
|
|
public SuspendContextImpl pushSuspendContext(final int suspendPolicy, int nVotes) {
|
|
SuspendContextImpl suspendContext = new SuspendContextImpl(myDebugProcess, suspendPolicy, nVotes, null) {
|
|
protected void resumeImpl() {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Start resuming...");
|
|
}
|
|
myDebugProcess.logThreads();
|
|
switch(getSuspendPolicy()) {
|
|
case EventRequest.SUSPEND_ALL:
|
|
try {
|
|
myDebugProcess.getVirtualMachineProxy().resume();
|
|
}
|
|
catch (InternalException e) {
|
|
//InternalException 13 means that there are running threads that we are trying to resume
|
|
//On MacOS it happened that native thread didn't stop while some java thread reached breakpoint
|
|
|
|
//Its funny, but second resume solves the problem
|
|
if (Patches.MAC_RESUME_VM_HACK && e.errorCode() == 13) {
|
|
myDebugProcess.getVirtualMachineProxy().resume();
|
|
}
|
|
else {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("VM resumed ");
|
|
}
|
|
break;
|
|
case EventRequest.SUSPEND_EVENT_THREAD:
|
|
getThread().resume();
|
|
if(LOG.isDebugEnabled()) {
|
|
LOG.debug("Thread resumed : " + getThread().toString());
|
|
}
|
|
break;
|
|
case EventRequest.SUSPEND_NONE:
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("None resumed");
|
|
}
|
|
break;
|
|
}
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Suspends = " + suspends);
|
|
}
|
|
myDebugProcess.logThreads();
|
|
}
|
|
};
|
|
pushContext(suspendContext);
|
|
return suspendContext;
|
|
}
|
|
|
|
public SuspendContextImpl pushSuspendContext(final EventSet set) {
|
|
SuspendContextImpl suspendContext = new SuspendContextImpl(myDebugProcess, set.suspendPolicy(), set.size(), set) {
|
|
protected void resumeImpl() {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Start resuming eventSet " + set.toString() + " suspendPolicy = " + set.suspendPolicy() + ",size = " + set.size());
|
|
}
|
|
myDebugProcess.logThreads();
|
|
LOG.assertTrue(getThread() != null ? getThread().isSuspended() : true);
|
|
try {
|
|
set.resume();
|
|
}
|
|
catch (InternalException e) {
|
|
//InternalException 13 means that there are running threads that we are trying to resume
|
|
//On MacOS it happened that native thread didn't stop while some java thread reached breakpoint
|
|
if (Patches.MAC_RESUME_VM_HACK && e.errorCode() == 13 && set.suspendPolicy() == EventRequest.SUSPEND_ALL) {
|
|
//Its funny, but second resume solves the problem
|
|
set.resume();
|
|
}
|
|
else {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Set resumed ");
|
|
}
|
|
myDebugProcess.logThreads();
|
|
}
|
|
};
|
|
pushContext(suspendContext);
|
|
return suspendContext;
|
|
}
|
|
|
|
private void pushContext(SuspendContextImpl suspendContext) {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
myEventContexts.addFirst(suspendContext);
|
|
suspends++;
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Push context : Suspends = " + suspends);
|
|
}
|
|
}
|
|
|
|
public void resume(SuspendContextImpl context) {
|
|
SuspendManagerUtil.prepareForResume(context);
|
|
|
|
myDebugProcess.logThreads();
|
|
int suspendPolicy = context.getSuspendPolicy();
|
|
context.resume();
|
|
popContext(context);
|
|
myDebugProcess.clearCashes(suspendPolicy);
|
|
}
|
|
|
|
|
|
public void popFrame(SuspendContextImpl suspendContext) {
|
|
popContext(suspendContext);
|
|
SuspendContextImpl newSuspendContext = pushSuspendContext(suspendContext.getSuspendPolicy(), 0);
|
|
newSuspendContext.setThread(suspendContext.getThread().getThreadReference());
|
|
notifyPaused(newSuspendContext);
|
|
}
|
|
|
|
public SuspendContextImpl getPausedContext() {
|
|
return !myPausedContexts.isEmpty() ? myPausedContexts.getFirst() : null;
|
|
}
|
|
|
|
public void popContext(SuspendContextImpl suspendContext) {
|
|
suspends--;
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("popContext, suspends = " + suspends);
|
|
}
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
boolean removed = myEventContexts.remove(suspendContext);
|
|
if(LOG.isDebugEnabled()) {
|
|
LOG.assertTrue(removed, suspendContext.toString());
|
|
}
|
|
myPausedContexts.remove(suspendContext);
|
|
}
|
|
|
|
void pushPausedContext(SuspendContextImpl suspendContext) {
|
|
if(LOG.isDebugEnabled()) {
|
|
LOG.assertTrue(myEventContexts.contains(suspendContext));
|
|
}
|
|
|
|
myPausedContexts.addFirst(suspendContext);
|
|
}
|
|
|
|
public boolean hasEventContext(SuspendContextImpl suspendContext) {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
return myEventContexts.contains(suspendContext);
|
|
}
|
|
|
|
public List<SuspendContextImpl> getEventContexts() {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
return myEventContexts;
|
|
}
|
|
|
|
public boolean isFrozen(ThreadReferenceProxyImpl thread) {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
return myFrozenThreads.contains(thread);
|
|
}
|
|
|
|
public boolean isSuspended(ThreadReferenceProxyImpl thread) {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
|
|
boolean suspended = false;
|
|
|
|
for (Iterator<SuspendContextImpl> iterator = myEventContexts.iterator(); iterator.hasNext();) {
|
|
SuspendContextImpl suspendContext = iterator.next();
|
|
|
|
if(suspendContext.suspends(thread) || isFrozen(thread)) {
|
|
suspended = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//bug in JDI : newly created thread may be resumed even when suspendPolicy == SUSPEND_ALL
|
|
//if(LOG.isDebugEnabled() && suspended) {
|
|
// LOG.assertTrue(thread.suspends(), thread.name());
|
|
//}
|
|
return suspended && thread.isSuspended();
|
|
}
|
|
|
|
public void suspendThread(SuspendContextImpl context, ThreadReferenceProxyImpl thread) {
|
|
LOG.assertTrue(thread != context.getThread(), "Thread is already suspended at the breakpoint");
|
|
|
|
if(context.isExplicitlyResumed(thread)) {
|
|
context.myResumedThreads.remove(thread);
|
|
thread.suspend();
|
|
}
|
|
}
|
|
|
|
public void resumeThread(SuspendContextImpl context, ThreadReferenceProxyImpl thread) {
|
|
LOG.assertTrue(thread != context.getThread(), "Use resume() instead of resuming breakpoint thread");
|
|
LOG.assertTrue(!context.isExplicitlyResumed(thread));
|
|
|
|
if(context.myResumedThreads == null) {
|
|
context.myResumedThreads = new HashSet<ThreadReferenceProxyImpl>();
|
|
}
|
|
context.myResumedThreads.add(thread);
|
|
thread.resume();
|
|
}
|
|
|
|
public void freezeThread(ThreadReferenceProxyImpl thread) {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
LOG.assertTrue(!isFrozen(thread));
|
|
myFrozenThreads.add(thread);
|
|
thread.suspend();
|
|
}
|
|
|
|
public void unfreezeThread(ThreadReferenceProxyImpl thread) {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
LOG.assertTrue(isFrozen(thread));
|
|
myFrozenThreads.remove(thread);
|
|
thread.resume();
|
|
}
|
|
|
|
private void processVote(SuspendContextImpl suspendContext) {
|
|
LOG.assertTrue(suspendContext.myVotesToVote > 0);
|
|
suspendContext.myVotesToVote--;
|
|
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("myVotesToVote = " + suspendContext.myVotesToVote);
|
|
}
|
|
if(suspendContext.myVotesToVote == 0) {
|
|
if(suspendContext.myIsVotedForResume) {
|
|
resume(suspendContext);
|
|
} else {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("vote paused");
|
|
}
|
|
myDebugProcess.logThreads();
|
|
myDebugProcess.cancelRunToCursorBreakpoint();
|
|
myDebugProcess.deleteStepRequests(suspendContext.getThread());
|
|
notifyPaused(suspendContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void notifyPaused(SuspendContextImpl suspendContext) {
|
|
pushPausedContext(suspendContext);
|
|
myDebugProcess.myDebugProcessDispatcher.getMulticaster().paused(suspendContext);
|
|
}
|
|
|
|
public void voteResume(SuspendContextImpl suspendContext) {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Resume voted");
|
|
}
|
|
processVote(suspendContext);
|
|
}
|
|
|
|
public void voteSuspend(SuspendContextImpl suspendContext) {
|
|
suspendContext.myIsVotedForResume = false;
|
|
processVote(suspendContext);
|
|
}
|
|
|
|
LinkedList<SuspendContextImpl> getPausedContexts() {
|
|
return myPausedContexts;
|
|
}
|
|
}
|