• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.tools.jdi;
27 
28 import com.sun.jdi.*;
29 
30 import java.lang.ref.WeakReference;
31 import java.util.*;
32 
33 class VMState {
34     private final VirtualMachineImpl vm;
35 
36     // Listeners
37     private final List<WeakReference<VMListener>> listeners = new ArrayList<WeakReference<VMListener>>(); // synchronized (this)
38     private boolean notifyingListeners = false;  // synchronized (this)
39 
40     /*
41      * Certain information can be cached only when the entire VM is
42      * suspended and there are no pending resumes. The fields below
43      * are used to track whether there are pending resumes. (There
44      * is an assumption that JDWP command ids are increasing over time.)
45      */
46     private int lastCompletedCommandId = 0;   // synchronized (this)
47     private int lastResumeCommandId = 0;      // synchronized (this)
48 
49     // This is cached only while the VM is suspended
50     private static class Cache {
51         List<ThreadGroupReference> groups = null;  // cached Top Level ThreadGroups
52         List<ThreadReference> threads = null; // cached Threads
53     }
54 
55     private Cache cache = null;               // synchronized (this)
56     private static final Cache markerCache = new Cache();
57 
disableCache()58     private void disableCache() {
59         synchronized (this) {
60             cache = null;
61         }
62     }
63 
enableCache()64     private void enableCache() {
65         synchronized (this) {
66             cache = markerCache;
67         }
68     }
69 
getCache()70     private Cache getCache() {
71         synchronized (this) {
72             if (cache == markerCache) {
73                 cache = new Cache();
74             }
75             return cache;
76         }
77     }
78 
VMState(VirtualMachineImpl vm)79     VMState(VirtualMachineImpl vm) {
80         this.vm = vm;
81     }
82 
83     /**
84      * Is the VM currently suspended, for the purpose of caching?
85      * Must be called synchronized on vm.state()
86      */
isSuspended()87     boolean isSuspended() {
88         return cache != null;
89     }
90 
91     /*
92      * A JDWP command has been completed (reply has been received).
93      * Update data that tracks pending resume commands.
94      */
notifyCommandComplete(int id)95     synchronized void notifyCommandComplete(int id) {
96         lastCompletedCommandId = id;
97     }
98 
freeze()99     synchronized void freeze() {
100         if (cache == null && (lastCompletedCommandId >= lastResumeCommandId)) {
101             /*
102              * No pending resumes to worry about. The VM is suspended
103              * and additional state can be cached. Notify all
104              * interested listeners.
105              */
106             processVMAction(new VMAction(vm, VMAction.VM_SUSPENDED));
107             enableCache();
108         }
109     }
110 
thawCommand(CommandSender sender)111     synchronized PacketStream thawCommand(CommandSender sender) {
112         PacketStream stream = sender.send();
113         lastResumeCommandId = stream.id();
114         thaw();
115         return stream;
116     }
117 
118     /**
119      * All threads are resuming
120      */
thaw()121     void thaw() {
122         thaw(null);
123     }
124 
125     /**
126      * Tell listeners to invalidate suspend-sensitive caches.
127      * If resumingThread != null, then only that thread is being
128      * resumed.
129      */
thaw(ThreadReference resumingThread)130     synchronized void thaw(ThreadReference resumingThread) {
131         if (cache != null) {
132             if ((vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) {
133                 vm.printTrace("Clearing VM suspended cache");
134             }
135             disableCache();
136         }
137         processVMAction(new VMAction(vm, resumingThread, VMAction.VM_NOT_SUSPENDED));
138     }
139 
processVMAction(VMAction action)140     private synchronized void processVMAction(VMAction action) {
141         if (!notifyingListeners) {
142             // Prevent recursion
143             notifyingListeners = true;
144 
145             Iterator<WeakReference<VMListener>> iter = listeners.iterator();
146             while (iter.hasNext()) {
147                 WeakReference<VMListener> ref = iter.next();
148                 VMListener listener = ref.get();
149                 if (listener != null) {
150                     boolean keep = true;
151                     switch (action.id()) {
152                         case VMAction.VM_SUSPENDED:
153                             keep = listener.vmSuspended(action);
154                             break;
155                         case VMAction.VM_NOT_SUSPENDED:
156                             keep = listener.vmNotSuspended(action);
157                             break;
158                     }
159                     if (!keep) {
160                         iter.remove();
161                     }
162                 } else {
163                     // Listener is unreachable; clean up
164                     iter.remove();
165                 }
166             }
167 
168             notifyingListeners = false;
169         }
170     }
171 
addListener(VMListener listener)172     synchronized void addListener(VMListener listener) {
173         listeners.add(new WeakReference<VMListener>(listener));
174     }
175 
hasListener(VMListener listener)176     synchronized boolean hasListener(VMListener listener) {
177         return listeners.contains(listener);
178     }
179 
removeListener(VMListener listener)180     synchronized void removeListener(VMListener listener) {
181         Iterator<WeakReference<VMListener>> iter = listeners.iterator();
182         while (iter.hasNext()) {
183             WeakReference<VMListener> ref = iter.next();
184             if (listener.equals(ref.get())) {
185                 iter.remove();
186                 break;
187             }
188         }
189     }
190 
allThreads()191     List<ThreadReference> allThreads() {
192         List<ThreadReference> threads = null;
193         try {
194             Cache local = getCache();
195 
196             if (local != null) {
197                 // may be stale when returned, but not provably so
198                 threads = local.threads;
199             }
200             if (threads == null) {
201                 threads = Arrays.asList((ThreadReference[])JDWP.VirtualMachine.AllThreads.
202                                         process(vm).threads);
203                 if (local != null) {
204                     local.threads = threads;
205                     if ((vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) {
206                         vm.printTrace("Caching all threads (count = " +
207                                       threads.size() + ") while VM suspended");
208                     }
209                 }
210             }
211         } catch (JDWPException exc) {
212             throw exc.toJDIException();
213         }
214         return threads;
215     }
216 
217 
topLevelThreadGroups()218     List<ThreadGroupReference> topLevelThreadGroups() {
219         List<ThreadGroupReference> groups = null;
220         try {
221             Cache local = getCache();
222 
223             if (local != null) {
224                 groups = local.groups;
225             }
226             if (groups == null) {
227                 groups = Arrays.asList(
228                                 (ThreadGroupReference[])JDWP.VirtualMachine.TopLevelThreadGroups.
229                                        process(vm).groups);
230                 if (local != null) {
231                     local.groups = groups;
232                     if ((vm.traceFlags & VirtualMachine.TRACE_OBJREFS) != 0) {
233                         vm.printTrace(
234                           "Caching top level thread groups (count = " +
235                           groups.size() + ") while VM suspended");
236                     }
237                 }
238             }
239         } catch (JDWPException exc) {
240             throw exc.toJDIException();
241         }
242         return groups;
243     }
244 
245 }
246