00aae83904ce59ebc8ceee1c8d6f1cf9f56db3cf
[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   @Override
130   public StackFrameProxyImpl getFrameProxy() {
131     assertNotResumed();
132     try {
133       return myThread != null && myThread.frameCount() > 0 ? myThread.frame(0) : null;
134     }
135     catch (EvaluateException ignored) {
136       return null;
137     }
138   }
139
140   @Override
141   public ThreadReferenceProxyImpl getThread() {
142     return myThread;
143   }
144
145   @Override
146   public int getSuspendPolicy() {
147     assertNotResumed();
148     return mySuspendPolicy;
149   }
150
151   public void doNotResumeHack() {
152     assertNotResumed();
153     myVotesToVote = 1000000000;
154   }
155
156   public boolean isExplicitlyResumed(ThreadReferenceProxyImpl thread) {
157     return myResumedThreads != null && myResumedThreads.contains(thread);
158   }
159
160   public boolean suspends(ThreadReferenceProxyImpl thread) {
161     assertNotResumed();
162     if(isEvaluating()) {
163       return false;
164     }
165     switch(getSuspendPolicy()) {
166       case EventRequest.SUSPEND_ALL:
167         return !isExplicitlyResumed(thread);
168       case EventRequest.SUSPEND_EVENT_THREAD:
169         return thread == getThread();
170     }
171     return false;
172   }
173
174   public boolean isEvaluating() {
175     assertNotResumed();
176     return myEvaluationContext != null;
177   }
178
179   public EvaluationContextImpl getEvaluationContext() {
180     return myEvaluationContext;
181   }
182
183   public boolean isResumed() {
184     return myIsResumed;
185   }
186
187   public void setIsEvaluating(EvaluationContextImpl evaluationContext) {
188     assertNotResumed();
189     myEvaluationContext = evaluationContext;
190   }
191
192   public String toString() {
193     if (myEventSet != null) {
194       return myEventSet.toString();
195     } 
196     return myThread != null ? myThread.toString() : DebuggerBundle.message("string.null.context");
197   }
198
199   public void keep(ObjectReference reference) {
200     if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
201       final boolean added = myKeptReferences.add(reference);
202       if (added) {
203         try {
204           reference.disableCollection();
205         }
206         catch (UnsupportedOperationException ignored) {
207           // ignore: some J2ME implementations does not provide this operation
208         }
209       }
210     }
211   }
212
213   public final void postponeCommand(final SuspendContextCommandImpl command) {
214     if (!isResumed()) {
215       // Important! when postponing increment the holds counter, so that the action is not released too early.
216       // This will ensure that the counter becomes zero only when the command is actually executed or canceled
217       command.hold();
218       myPostponedCommands.add(command);
219     }
220     else {
221       command.notifyCancelled();
222     }
223   }
224
225   public final SuspendContextCommandImpl pollPostponedCommand() {
226     return myPostponedCommands.poll();
227   }
228
229   @Nullable
230   @Override
231   public XExecutionStack getActiveExecutionStack() {
232     return myActiveExecutionStack;
233   }
234
235   public void initExecutionStacks(ThreadReferenceProxyImpl newThread) {
236     DebuggerManagerThreadImpl.assertIsManagerThread();
237     myThread = newThread;
238     if (newThread != null) {
239       myActiveExecutionStack = new JavaExecutionStack(newThread, myDebugProcess, true);
240     }
241   }
242
243   @Override
244   public void computeExecutionStacks(final XExecutionStackContainer container) {
245     myDebugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(this) {
246       @Override
247       public void contextAction() throws Exception {
248         List<JavaExecutionStack> res = new ArrayList<JavaExecutionStack>();
249         Collection<ThreadReferenceProxyImpl> threads = getDebugProcess().getVirtualMachineProxy().allThreads();
250         JavaExecutionStack currentStack = null;
251         for (ThreadReferenceProxyImpl thread : threads) {
252           boolean current = thread == myThread;
253           JavaExecutionStack stack = new JavaExecutionStack(thread, myDebugProcess, current);
254           if (!current) {
255             res.add(stack);
256           }
257           else {
258             currentStack = stack;
259           }
260         }
261         Collections.sort(res, THREADS_COMPARATOR);
262         if (currentStack != null) {
263           res.add(0, currentStack);
264         }
265         container.addExecutionStack(res, true);
266       }
267     });
268   }
269
270   private static final Comparator<JavaExecutionStack> THREADS_COMPARATOR = new Comparator<JavaExecutionStack>() {
271     @Override
272     public int compare(JavaExecutionStack th1, JavaExecutionStack th2) {
273       return th1.getDisplayName().compareToIgnoreCase(th2.getDisplayName());
274     }
275   };
276 }