IDEA-131931 Debugger: Throwable at SuspendContextImpl.getDebugProcess() on opening...
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / engine / SuspendContextImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.debugger.engine;
17
18 import com.intellij.Patches;
19 import com.intellij.debugger.DebuggerBundle;
20 import com.intellij.debugger.engine.evaluation.EvaluateException;
21 import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
22 import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
23 import com.intellij.debugger.jdi.StackFrameProxyImpl;
24 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.util.containers.HashSet;
27 import com.intellij.xdebugger.frame.XExecutionStack;
28 import com.intellij.xdebugger.frame.XSuspendContext;
29 import com.sun.jdi.ObjectReference;
30 import com.sun.jdi.ThreadReference;
31 import com.sun.jdi.event.EventSet;
32 import com.sun.jdi.request.EventRequest;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
35
36 import java.util.*;
37 import java.util.concurrent.ConcurrentLinkedQueue;
38
39 /**
40  * @author lex
41  */
42 public abstract class SuspendContextImpl extends XSuspendContext implements SuspendContext {
43   private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.SuspendContextImpl");
44
45   private final DebugProcessImpl myDebugProcess;
46   private final int mySuspendPolicy;
47
48   private ThreadReferenceProxyImpl myThread;
49   boolean myIsVotedForResume = true;
50
51   protected int myVotesToVote;
52   protected Set<ThreadReferenceProxyImpl> myResumedThreads;
53
54   private final EventSet myEventSet;
55   private volatile boolean myIsResumed;
56
57   public ConcurrentLinkedQueue<SuspendContextCommandImpl> myPostponedCommands = new ConcurrentLinkedQueue<SuspendContextCommandImpl>();
58   public volatile boolean myInProgress;
59   private final HashSet<ObjectReference> myKeptReferences = new HashSet<ObjectReference>();
60   private EvaluationContextImpl myEvaluationContext = null;
61
62   private JavaExecutionStack myActiveExecutionStack;
63
64   SuspendContextImpl(@NotNull DebugProcessImpl debugProcess, int suspendPolicy, int eventVotes, EventSet set) {
65     myDebugProcess = debugProcess;
66     mySuspendPolicy = suspendPolicy;
67     myVotesToVote = eventVotes;
68     myEventSet = set;
69   }
70
71   public void setThread(ThreadReference thread) {
72     assertNotResumed();
73     ThreadReferenceProxyImpl threadProxy = myDebugProcess.getVirtualMachineProxy().getThreadReferenceProxy(thread);
74     LOG.assertTrue(myThread == null || myThread == threadProxy);
75     myThread = threadProxy;
76   }
77
78   protected abstract void resumeImpl();
79
80   protected void resume(){
81     assertNotResumed();
82     DebuggerManagerThreadImpl.assertIsManagerThread();
83     try {
84       if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
85         for (ObjectReference objectReference : myKeptReferences) {
86           try {
87             objectReference.enableCollection();
88           }
89           catch (UnsupportedOperationException ignored) {
90             // ignore: some J2ME implementations does not provide this operation
91           }
92         }
93         myKeptReferences.clear();
94       }
95
96       for(SuspendContextCommandImpl cmd = pollPostponedCommand(); cmd != null; cmd = pollPostponedCommand()) {
97         cmd.notifyCancelled();
98       }
99
100       resumeImpl();
101     }
102     finally {
103       myIsResumed = true;
104     }
105   }
106
107   private void assertNotResumed() {
108     if (myIsResumed) {
109       if (myDebugProcess.isAttached()) {
110         LOG.error("Cannot access SuspendContext. SuspendContext is resumed.");
111       }
112     }
113   }
114
115
116   @Nullable
117   public EventSet getEventSet() {
118     assertNotResumed();
119     return myEventSet;
120   }
121
122   @Override
123   @NotNull
124   public DebugProcessImpl getDebugProcess() {
125     assertNotResumed();
126     return myDebugProcess;
127   }
128
129   public DebugProcessImpl getDebugProcessNoAssert() {
130     return myDebugProcess;
131   }
132
133   @Override
134   public StackFrameProxyImpl getFrameProxy() {
135     assertNotResumed();
136     try {
137       return myThread != null && myThread.frameCount() > 0 ? myThread.frame(0) : null;
138     }
139     catch (EvaluateException ignored) {
140       return null;
141     }
142   }
143
144   @Override
145   public ThreadReferenceProxyImpl getThread() {
146     return myThread;
147   }
148
149   @Override
150   public int getSuspendPolicy() {
151     assertNotResumed();
152     return mySuspendPolicy;
153   }
154
155   public void doNotResumeHack() {
156     assertNotResumed();
157     myVotesToVote = 1000000000;
158   }
159
160   public boolean isExplicitlyResumed(ThreadReferenceProxyImpl thread) {
161     return myResumedThreads != null && myResumedThreads.contains(thread);
162   }
163
164   public boolean suspends(ThreadReferenceProxyImpl thread) {
165     assertNotResumed();
166     if(isEvaluating()) {
167       return false;
168     }
169     switch(getSuspendPolicy()) {
170       case EventRequest.SUSPEND_ALL:
171         return !isExplicitlyResumed(thread);
172       case EventRequest.SUSPEND_EVENT_THREAD:
173         return thread == getThread();
174     }
175     return false;
176   }
177
178   public boolean isEvaluating() {
179     assertNotResumed();
180     return myEvaluationContext != null;
181   }
182
183   public EvaluationContextImpl getEvaluationContext() {
184     return myEvaluationContext;
185   }
186
187   public boolean isResumed() {
188     return myIsResumed;
189   }
190
191   public void setIsEvaluating(EvaluationContextImpl evaluationContext) {
192     assertNotResumed();
193     myEvaluationContext = evaluationContext;
194   }
195
196   public String toString() {
197     if (myEventSet != null) {
198       return myEventSet.toString();
199     } 
200     return myThread != null ? myThread.toString() : DebuggerBundle.message("string.null.context");
201   }
202
203   public void keep(ObjectReference reference) {
204     if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
205       final boolean added = myKeptReferences.add(reference);
206       if (added) {
207         try {
208           reference.disableCollection();
209         }
210         catch (UnsupportedOperationException ignored) {
211           // ignore: some J2ME implementations does not provide this operation
212         }
213       }
214     }
215   }
216
217   public final void postponeCommand(final SuspendContextCommandImpl command) {
218     if (!isResumed()) {
219       // Important! when postponing increment the holds counter, so that the action is not released too early.
220       // This will ensure that the counter becomes zero only when the command is actually executed or canceled
221       command.hold();
222       myPostponedCommands.add(command);
223     }
224     else {
225       command.notifyCancelled();
226     }
227   }
228
229   public final SuspendContextCommandImpl pollPostponedCommand() {
230     return myPostponedCommands.poll();
231   }
232
233   @Nullable
234   @Override
235   public XExecutionStack getActiveExecutionStack() {
236     return myActiveExecutionStack;
237   }
238
239   public void initExecutionStacks(ThreadReferenceProxyImpl newThread) {
240     DebuggerManagerThreadImpl.assertIsManagerThread();
241     myThread = newThread;
242     if (newThread != null) {
243       myActiveExecutionStack = new JavaExecutionStack(newThread, myDebugProcess, true);
244     }
245   }
246
247   @Override
248   public void computeExecutionStacks(final XExecutionStackContainer container) {
249     myDebugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(this) {
250       @Override
251       public void contextAction() throws Exception {
252         List<JavaExecutionStack> res = new ArrayList<JavaExecutionStack>();
253         Collection<ThreadReferenceProxyImpl> threads = getDebugProcess().getVirtualMachineProxy().allThreads();
254         JavaExecutionStack currentStack = null;
255         for (ThreadReferenceProxyImpl thread : threads) {
256           boolean current = thread == myThread;
257           JavaExecutionStack stack = new JavaExecutionStack(thread, myDebugProcess, current);
258           if (!current) {
259             res.add(stack);
260           }
261           else {
262             currentStack = stack;
263           }
264         }
265         Collections.sort(res, THREADS_COMPARATOR);
266         if (currentStack != null) {
267           res.add(0, currentStack);
268         }
269         container.addExecutionStack(res, true);
270       }
271     });
272   }
273
274   private static final Comparator<JavaExecutionStack> THREADS_COMPARATOR = new Comparator<JavaExecutionStack>() {
275     @Override
276     public int compare(JavaExecutionStack th1, JavaExecutionStack th2) {
277       return th1.getDisplayName().compareToIgnoreCase(th2.getDisplayName());
278     }
279   };
280 }