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