Showing the changes to java/pkg/com/sun/tools/jdi/ThreadReferenceImpl.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,644 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 2008, 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 | import com.sun.jdi.request.BreakpointRequest; | ||
| 30 | import java.util.*; | ||
| 31 | import java.lang.ref.WeakReference; | ||
| 32 | |||
| 33 | public class ThreadReferenceImpl extends ObjectReferenceImpl | ||
| 34 | implements ThreadReference, VMListener { | ||
| 35 | static final int SUSPEND_STATUS_SUSPENDED = 0x1; | ||
| 36 | static final int SUSPEND_STATUS_BREAK = 0x2; | ||
| 37 | |||
| 38 | private int suspendedZombieCount = 0; | ||
| 39 | |||
| 40 | /* | ||
| 41 | * Some objects can only be created while a thread is suspended and are valid | ||
| 42 | * only while the thread remains suspended. Examples are StackFrameImpl | ||
| 43 | * and MonitorInfoImpl. When the thread resumes, these objects have to be | ||
| 44 | * marked as invalid so that their methods can throw | ||
| 45 | * InvalidStackFrameException if they are called. To do this, such objects | ||
| 46 | * register themselves as listeners of the associated thread. When the | ||
| 47 | * thread is resumed, its listeners are notified and mark themselves | ||
| 48 | * invalid. | ||
| 49 | * Also, note that ThreadReferenceImpl itself caches some info that | ||
| 50 | * is valid only as long as the thread is suspended. When the thread | ||
| 51 | * is resumed, that cache must be purged. | ||
| 52 | * Lastly, note that ThreadReferenceImpl and its super, ObjectReferenceImpl | ||
| 53 | * cache some info that is only valid as long as the entire VM is suspended. | ||
| 54 | * If _any_ thread is resumed, this cache must be purged. To handle this, | ||
| 55 | * both ThreadReferenceImpl and ObjectReferenceImpl register themselves as | ||
| 56 | * VMListeners so that they get notified when all threads are suspended and | ||
| 57 | * when any thread is resumed. | ||
| 58 | */ | ||
| 59 | |||
| 60 | // This is cached for the life of the thread | ||
| 61 | private ThreadGroupReference threadGroup; | ||
| 62 | |||
| 63 | // This is cached only while this one thread is suspended. Each time | ||
| 64 | // the thread is resumed, we abandon the current cache object and | ||
| 65 | // create a new intialized one. | ||
| 66 | private static class LocalCache { | ||
| 67 | JDWP.ThreadReference.Status status = null; | ||
| 68 | List<StackFrame> frames = null; | ||
| 69 | int framesStart = -1; | ||
| 70 | int framesLength = 0; | ||
| 71 | int frameCount = -1; | ||
| 72 | List<ObjectReference> ownedMonitors = null; | ||
| 73 | List<MonitorInfo> ownedMonitorsInfo = null; | ||
| 74 | ObjectReference contendedMonitor = null; | ||
| 75 | boolean triedCurrentContended = false; | ||
| 76 | } | ||
| 77 | |||
| 78 | /* | ||
| 79 | * The localCache instance var is set by resetLocalCache to an initialized | ||
| 80 | * object as shown above. This occurs when the ThreadReference | ||
| 81 | * object is created, and when the mirrored thread is resumed. | ||
| 82 | * The fields are then filled in by the relevant methods as they | ||
| 83 | * are called. A problem can occur if resetLocalCache is called | ||
| 84 | * (ie, a resume() is executed) at certain points in the execution | ||
| 85 | * of some of these methods - see 6751643. To avoid this, each | ||
| 86 | * method that wants to use this cache must make a local copy of | ||
| 87 | * this variable and use that. This means that each invocation of | ||
| 88 | * these methods will use a copy of the cache object that was in | ||
| 89 | * effect at the point that the copy was made; if a racy resume | ||
| 90 | * occurs, it won't affect the method's local copy. This means that | ||
| 91 | * the values returned by these calls may not match the state of | ||
| 92 | * the debuggee at the time the caller gets the values. EG, | ||
| 93 | * frameCount() is called and comes up with 5 frames. But before | ||
| 94 | * it returns this, a resume of the debuggee thread is executed in a | ||
| 95 | * different debugger thread. The thread is resumed and running at | ||
| 96 | * the time that the value 5 is returned. Or even worse, the thread | ||
| 97 | * could be suspended again and have a different number of frames, eg, 24, | ||
| 98 | * but this call will still return 5. | ||
| 99 | */ | ||
| 100 | private LocalCache localCache; | ||
| 101 | |||
| 102 | private void resetLocalCache() { | ||
| 103 | localCache = new LocalCache(); | ||
| 104 | } | ||
| 105 | |||
| 106 | // This is cached only while all threads in the VM are suspended | ||
| 107 | // Yes, someone could change the name of a thread while it is suspended. | ||
| 108 | private static class Cache extends ObjectReferenceImpl.Cache { | ||
| 109 | String name = null; | ||
| 110 | } | ||
| 111 | protected ObjectReferenceImpl.Cache newCache() { | ||
| 112 | return new Cache(); | ||
| 113 | } | ||
| 114 | |||
| 115 | // Listeners - synchronized on vm.state() | ||
| 116 | private List<WeakReference<ThreadListener>> listeners = new ArrayList<WeakReference<ThreadListener>>(); | ||
| 117 | |||
| 118 | |||
| 119 | ThreadReferenceImpl(VirtualMachine aVm, long aRef) { | ||
| 120 | super(aVm,aRef); | ||
| 121 | resetLocalCache(); | ||
| 122 | vm.state().addListener(this); | ||
| 123 | } | ||
| 124 | |||
| 125 | protected String description() { | ||
| 126 | return "ThreadReference " + uniqueID(); | ||
| 127 | } | ||
| 128 | |||
| 129 | /* | ||
| 130 | * VMListener implementation | ||
| 131 | */ | ||
| 132 | public boolean vmNotSuspended(VMAction action) { | ||
| 133 | if (action.resumingThread() == null) { | ||
| 134 | // all threads are being resumed | ||
| 135 | synchronized (vm.state()) { | ||
| 136 | processThreadAction(new ThreadAction(this, | ||
| 137 | ThreadAction.THREAD_RESUMABLE)); | ||
| 138 | } | ||
| 139 | |||
| 140 | } | ||
| 141 | |||
| 142 | /* | ||
| 143 | * Othewise, only one thread is being resumed: | ||
| 144 | * if it is us, | ||
| 145 | * we have already done our processThreadAction to notify our | ||
| 146 | * listeners when we processed the resume. | ||
| 147 | * if it is not us, | ||
| 148 | * we don't want to notify our listeners | ||
| 149 | * because we are not being resumed. | ||
| 150 | */ | ||
| 151 | return super.vmNotSuspended(action); | ||
| 152 | } | ||
| 153 | |||
| 154 | /** | ||
| 155 | * Note that we only cache the name string while the entire VM is suspended | ||
| 156 | * because the name can change via Thread.setName arbitrarily while this | ||
| 157 | * thread is running. | ||
| 158 | */ | ||
| 159 | public String name() { | ||
| 160 | String name = null; | ||
| 161 | try { | ||
| 162 | Cache local = (Cache)getCache(); | ||
| 163 | |||
| 164 | if (local != null) { | ||
| 165 | name = local.name; | ||
| 166 | } | ||
| 167 | if (name == null) { | ||
| 168 | name = JDWP.ThreadReference.Name.process(vm, this) | ||
| 169 | .threadName; | ||
| 170 | if (local != null) { | ||
| 171 | local.name = name; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | } catch (JDWPException exc) { | ||
| 175 | throw exc.toJDIException(); | ||
| 176 | } | ||
| 177 | return name; | ||
| 178 | } | ||
| 179 | |||
| 180 | /* | ||
| 181 | * Sends a command to the back end which is defined to do an | ||
| 182 | * implicit vm-wide resume. | ||
| 183 | */ | ||
| 184 | PacketStream sendResumingCommand(CommandSender sender) { | ||
| 185 | synchronized (vm.state()) { | ||
| 186 | processThreadAction(new ThreadAction(this, | ||
| 187 | ThreadAction.THREAD_RESUMABLE)); | ||
| 188 | return sender.send(); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | public void suspend() { | ||
| 193 | try { | ||
| 194 | JDWP.ThreadReference.Suspend.process(vm, this); | ||
| 195 | } catch (JDWPException exc) { | ||
| 196 | throw exc.toJDIException(); | ||
| 197 | } | ||
| 198 | // Don't consider the thread suspended yet. On reply, notifySuspend() | ||
| 199 | // will be called. | ||
| 200 | } | ||
| 201 | |||
| 202 | public void resume() { | ||
| 203 | /* | ||
| 204 | * If it's a zombie, we can just update internal state without | ||
| 205 | * going to back end. | ||
| 206 | */ | ||
| 207 | if (suspendedZombieCount > 0) { | ||
| 208 | suspendedZombieCount--; | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | |||
| 212 | PacketStream stream; | ||
| 213 | synchronized (vm.state()) { | ||
| 214 | processThreadAction(new ThreadAction(this, | ||
| 215 | ThreadAction.THREAD_RESUMABLE)); | ||
| 216 | stream = JDWP.ThreadReference.Resume.enqueueCommand(vm, this); | ||
| 217 | } | ||
| 218 | try { | ||
| 219 | JDWP.ThreadReference.Resume.waitForReply(vm, stream); | ||
| 220 | } catch (JDWPException exc) { | ||
| 221 | throw exc.toJDIException(); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | public int suspendCount() { | ||
| 226 | /* | ||
| 227 | * If it's a zombie, we maintain the count in the front end. | ||
| 228 | */ | ||
| 229 | if (suspendedZombieCount > 0) { | ||
| 230 | return suspendedZombieCount; | ||
| 231 | } | ||
| 232 | |||
| 233 | try { | ||
| 234 | return JDWP.ThreadReference.SuspendCount.process(vm, this).suspendCount; | ||
| 235 | } catch (JDWPException exc) { | ||
| 236 | throw exc.toJDIException(); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | public void stop(ObjectReference throwable) throws InvalidTypeException { | ||
| 241 | validateMirror(throwable); | ||
| 242 | // Verify that the given object is a Throwable instance | ||
| 243 | List list = vm.classesByName("java.lang.Throwable"); | ||
| 244 | ClassTypeImpl throwableClass = (ClassTypeImpl)list.get(0); | ||
| 245 | if ((throwable == null) || | ||
| 246 | !throwableClass.isAssignableFrom(throwable)) { | ||
| 247 | throw new InvalidTypeException("Not an instance of Throwable"); | ||
| 248 | } | ||
| 249 | |||
| 250 | try { | ||
| 251 | JDWP.ThreadReference.Stop.process(vm, this, | ||
| 252 | (ObjectReferenceImpl)throwable); | ||
| 253 | } catch (JDWPException exc) { | ||
| 254 | throw exc.toJDIException(); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | public void interrupt() { | ||
| 259 | try { | ||
| 260 | JDWP.ThreadReference.Interrupt.process(vm, this); | ||
| 261 | } catch (JDWPException exc) { | ||
| 262 | throw exc.toJDIException(); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | private JDWP.ThreadReference.Status jdwpStatus() { | ||
| 267 | LocalCache snapshot = localCache; | ||
| 268 | JDWP.ThreadReference.Status myStatus = snapshot.status; | ||
| 269 | try { | ||
| 270 | if (myStatus == null) { | ||
| 271 | myStatus = JDWP.ThreadReference.Status.process(vm, this); | ||
| 272 | if ((myStatus.suspendStatus & SUSPEND_STATUS_SUSPENDED) != 0) { | ||
| 273 | // thread is suspended, we can cache the status. | ||
| 274 | snapshot.status = myStatus; | ||
| 275 | } | ||
| 276 | } | ||
| 277 | } catch (JDWPException exc) { | ||
| 278 | throw exc.toJDIException(); | ||
| 279 | } | ||
| 280 | return myStatus; | ||
| 281 | } | ||
| 282 | |||
| 283 | public int status() { | ||
| 284 | return jdwpStatus().threadStatus; | ||
| 285 | } | ||
| 286 | |||
| 287 | public boolean isSuspended() { | ||
| 288 | return ((suspendedZombieCount > 0) || | ||
| 289 | ((jdwpStatus().suspendStatus & SUSPEND_STATUS_SUSPENDED) != 0)); | ||
| 290 | } | ||
| 291 | |||
| 292 | public boolean isAtBreakpoint() { | ||
| 293 | /* | ||
| 294 | * TO DO: This fails to take filters into account. | ||
| 295 | */ | ||
| 296 | try { | ||
| 297 | StackFrame frame = frame(0); | ||
| 298 | Location location = frame.location(); | ||
| 299 | List requests = vm.eventRequestManager().breakpointRequests(); | ||
| 300 | Iterator iter = requests.iterator(); | ||
| 301 | while (iter.hasNext()) { | ||
| 302 | BreakpointRequest request = (BreakpointRequest)iter.next(); | ||
| 303 | if (location.equals(request.location())) { | ||
| 304 | return true; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | return false; | ||
| 308 | } catch (IndexOutOfBoundsException iobe) { | ||
| 309 | return false; // no frames on stack => not at breakpoint | ||
| 310 | } catch (IncompatibleThreadStateException itse) { | ||
| 311 | // Per the javadoc, not suspended => return false | ||
| 312 | return false; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | public ThreadGroupReference threadGroup() { | ||
| 317 | /* | ||
| 318 | * Thread group can't change, so it's cached once and for all. | ||
| 319 | */ | ||
| 320 | if (threadGroup == null) { | ||
| 321 | try { | ||
| 322 | threadGroup = JDWP.ThreadReference.ThreadGroup. | ||
| 323 | process(vm, this).group; | ||
| 324 | } catch (JDWPException exc) { | ||
| 325 | throw exc.toJDIException(); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | return threadGroup; | ||
| 329 | } | ||
| 330 | |||
| 331 | public int frameCount() throws IncompatibleThreadStateException { | ||
| 332 | LocalCache snapshot = localCache; | ||
| 333 | try { | ||
| 334 | if (snapshot.frameCount == -1) { | ||
| 335 | snapshot.frameCount = JDWP.ThreadReference.FrameCount | ||
| 336 | .process(vm, this).frameCount; | ||
| 337 | } | ||
| 338 | } catch (JDWPException exc) { | ||
| 339 | switch (exc.errorCode()) { | ||
| 340 | case JDWP.Error.THREAD_NOT_SUSPENDED: | ||
| 341 | case JDWP.Error.INVALID_THREAD: /* zombie */ | ||
| 342 | throw new IncompatibleThreadStateException(); | ||
| 343 | default: | ||
| 344 | throw exc.toJDIException(); | ||
| 345 | } | ||
| 346 | } | ||
| 347 | return snapshot.frameCount; | ||
| 348 | } | ||
| 349 | |||
| 350 | public List<StackFrame> frames() throws IncompatibleThreadStateException { | ||
| 351 | return privateFrames(0, -1); | ||
| 352 | } | ||
| 353 | |||
| 354 | public StackFrame frame(int index) throws IncompatibleThreadStateException { | ||
| 355 | List list = privateFrames(index, 1); | ||
| 356 | return (StackFrame)list.get(0); | ||
| 357 | } | ||
| 358 | |||
| 359 | /** | ||
| 360 | * Is the requested subrange within what has been retrieved? | ||
| 361 | * local is known to be non-null. Should only be called from | ||
| 362 | * a sync method. | ||
| 363 | */ | ||
| 364 | private boolean isSubrange(LocalCache snapshot, | ||
| 365 | int start, int length) { | ||
| 366 | if (start < snapshot.framesStart) { | ||
| 367 | return false; | ||
| 368 | } | ||
| 369 | if (length == -1) { | ||
| 370 | return (snapshot.framesLength == -1); | ||
| 371 | } | ||
| 372 | if (snapshot.framesLength == -1) { | ||
| 373 | if ((start + length) > (snapshot.framesStart + | ||
| 374 | snapshot.frames.size())) { | ||
| 375 | throw new IndexOutOfBoundsException(); | ||
| 376 | } | ||
| 377 | return true; | ||
| 378 | } | ||
| 379 | return ((start + length) <= (snapshot.framesStart + snapshot.framesLength)); | ||
| 380 | } | ||
| 381 | |||
| 382 | public List<StackFrame> frames(int start, int length) | ||
| 383 | throws IncompatibleThreadStateException { | ||
| 384 | if (length < 0) { | ||
| 385 | throw new IndexOutOfBoundsException( | ||
| 386 | "length must be greater than or equal to zero"); | ||
| 387 | } | ||
| 388 | return privateFrames(start, length); | ||
| 389 | } | ||
| 390 | |||
| 391 | /** | ||
| 392 | * Private version of frames() allows "-1" to specify all | ||
| 393 | * remaining frames. | ||
| 394 | */ | ||
| 395 | synchronized private List<StackFrame> privateFrames(int start, int length) | ||
| 396 | throws IncompatibleThreadStateException { | ||
| 397 | |||
| 398 | // Lock must be held while creating stack frames so if that two threads | ||
| 399 | // do this at the same time, one won't clobber the subset created by the other. | ||
| 400 | LocalCache snapshot = localCache; | ||
| 401 | try { | ||
| 402 | if (snapshot.frames == null || !isSubrange(snapshot, start, length)) { | ||
| 403 | JDWP.ThreadReference.Frames.Frame[] jdwpFrames | ||
| 404 | = JDWP.ThreadReference.Frames. | ||
| 405 | process(vm, this, start, length).frames; | ||
| 406 | int count = jdwpFrames.length; | ||
| 407 | snapshot.frames = new ArrayList<StackFrame>(count); | ||
| 408 | |||
| 409 | for (int i = 0; i<count; i++) { | ||
| 410 | if (jdwpFrames[i].location == null) { | ||
| 411 | throw new InternalException("Invalid frame location"); | ||
| 412 | } | ||
| 413 | StackFrame frame = new StackFrameImpl(vm, this, | ||
| 414 | jdwpFrames[i].frameID, | ||
| 415 | jdwpFrames[i].location); | ||
| 416 | // Add to the frame list | ||
| 417 | snapshot.frames.add(frame); | ||
| 418 | } | ||
| 419 | snapshot.framesStart = start; | ||
| 420 | snapshot.framesLength = length; | ||
| 421 | return Collections.unmodifiableList(snapshot.frames); | ||
| 422 | } else { | ||
| 423 | int fromIndex = start - snapshot.framesStart; | ||
| 424 | int toIndex; | ||
| 425 | if (length == -1) { | ||
| 426 | toIndex = snapshot.frames.size() - fromIndex; | ||
| 427 | } else { | ||
| 428 | toIndex = fromIndex + length; | ||
| 429 | } | ||
| 430 | return Collections.unmodifiableList(snapshot.frames.subList(fromIndex, toIndex)); | ||
| 431 | } | ||
| 432 | } catch (JDWPException exc) { | ||
| 433 | switch (exc.errorCode()) { | ||
| 434 | case JDWP.Error.THREAD_NOT_SUSPENDED: | ||
| 435 | case JDWP.Error.INVALID_THREAD: /* zombie */ | ||
| 436 | throw new IncompatibleThreadStateException(); | ||
| 437 | default: | ||
| 438 | throw exc.toJDIException(); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | } | ||
| 442 | |||
| 443 | public List<ObjectReference> ownedMonitors() throws IncompatibleThreadStateException { | ||
| 444 | LocalCache snapshot = localCache; | ||
| 445 | try { | ||
| 446 | if (snapshot.ownedMonitors == null) { | ||
| 447 | snapshot.ownedMonitors = Arrays.asList( | ||
| 448 | (ObjectReference[])JDWP.ThreadReference.OwnedMonitors. | ||
| 449 | process(vm, this).owned); | ||
| 450 | if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { | ||
| 451 | vm.printTrace(description() + | ||
| 452 | " temporarily caching owned monitors"+ | ||
| 453 | " (count = " + snapshot.ownedMonitors.size() + ")"); | ||
| 454 | } | ||
| 455 | } | ||
| 456 | } catch (JDWPException exc) { | ||
| 457 | switch (exc.errorCode()) { | ||
| 458 | case JDWP.Error.THREAD_NOT_SUSPENDED: | ||
| 459 | case JDWP.Error.INVALID_THREAD: /* zombie */ | ||
| 460 | throw new IncompatibleThreadStateException(); | ||
| 461 | default: | ||
| 462 | throw exc.toJDIException(); | ||
| 463 | } | ||
| 464 | } | ||
| 465 | return snapshot.ownedMonitors; | ||
| 466 | } | ||
| 467 | |||
| 468 | public ObjectReference currentContendedMonitor() | ||
| 469 | throws IncompatibleThreadStateException { | ||
| 470 | LocalCache snapshot = localCache; | ||
| 471 | try { | ||
| 472 | if (snapshot.contendedMonitor == null && | ||
| 473 | !snapshot.triedCurrentContended) { | ||
| 474 | snapshot.contendedMonitor = JDWP.ThreadReference.CurrentContendedMonitor. | ||
| 475 | process(vm, this).monitor; | ||
| 476 | snapshot.triedCurrentContended = true; | ||
| 477 | if ((snapshot.contendedMonitor != null) && | ||
| 478 | ((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) { | ||
| 479 | vm.printTrace(description() + | ||
| 480 | " temporarily caching contended monitor"+ | ||
| 481 | " (id = " + snapshot.contendedMonitor.uniqueID() + ")"); | ||
| 482 | } | ||
| 483 | } | ||
| 484 | } catch (JDWPException exc) { | ||
| 485 | switch (exc.errorCode()) { | ||
| 486 | case JDWP.Error.THREAD_NOT_SUSPENDED: | ||
| 487 | case JDWP.Error.INVALID_THREAD: /* zombie */ | ||
| 488 | throw new IncompatibleThreadStateException(); | ||
| 489 | default: | ||
| 490 | throw exc.toJDIException(); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | return snapshot.contendedMonitor; | ||
| 494 | } | ||
| 495 | |||
| 496 | public List<MonitorInfo> ownedMonitorsAndFrames() throws IncompatibleThreadStateException { | ||
| 497 | LocalCache snapshot = localCache; | ||
| 498 | try { | ||
| 499 | if (snapshot.ownedMonitorsInfo == null) { | ||
| 500 | JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor[] minfo; | ||
| 501 | minfo = JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.process(vm, this).owned; | ||
| 502 | |||
| 503 | snapshot.ownedMonitorsInfo = new ArrayList<MonitorInfo>(minfo.length); | ||
| 504 | |||
| 505 | for (int i=0; i < minfo.length; i++) { | ||
| 506 | JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor mi = | ||
| 507 | minfo[i]; | ||
| 508 | MonitorInfo mon = new MonitorInfoImpl(vm, minfo[i].monitor, this, minfo[i].stack_depth); | ||
| 509 | snapshot.ownedMonitorsInfo.add(mon); | ||
| 510 | } | ||
| 511 | |||
| 512 | if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { | ||
| 513 | vm.printTrace(description() + | ||
| 514 | " temporarily caching owned monitors"+ | ||
| 515 | " (count = " + snapshot.ownedMonitorsInfo.size() + ")"); | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | } catch (JDWPException exc) { | ||
| 520 | switch (exc.errorCode()) { | ||
| 521 | case JDWP.Error.THREAD_NOT_SUSPENDED: | ||
| 522 | case JDWP.Error.INVALID_THREAD: /* zombie */ | ||
| 523 | throw new IncompatibleThreadStateException(); | ||
| 524 | default: | ||
| 525 | throw exc.toJDIException(); | ||
| 526 | } | ||
| 527 | } | ||
| 528 | return snapshot.ownedMonitorsInfo; | ||
| 529 | } | ||
| 530 | |||
| 531 | public void popFrames(StackFrame frame) throws IncompatibleThreadStateException { | ||
| 532 | // Note that interface-wise this functionality belongs | ||
| 533 | // here in ThreadReference, but implementation-wise it | ||
| 534 | // belongs in StackFrame, so we just forward it. | ||
| 535 | if (!frame.thread().equals(this)) { | ||
| 536 | throw new IllegalArgumentException("frame does not belong to this thread"); | ||
| 537 | } | ||
| 538 | if (!vm.canPopFrames()) { | ||
| 539 | throw new UnsupportedOperationException( | ||
| 540 | "target does not support popping frames"); | ||
| 541 | } | ||
| 542 | ((StackFrameImpl)frame).pop(); | ||
| 543 | } | ||
| 544 | |||
| 545 | public void forceEarlyReturn(Value returnValue) throws InvalidTypeException, | ||
| 546 | ClassNotLoadedException, | ||
| 547 | IncompatibleThreadStateException { | ||
| 548 | if (!vm.canForceEarlyReturn()) { | ||
| 549 | throw new UnsupportedOperationException( | ||
| 550 | "target does not support the forcing of a method to return early"); | ||
| 551 | } | ||
| 552 | |||
| 553 | validateMirrorOrNull(returnValue); | ||
| 554 | |||
| 555 | StackFrameImpl sf; | ||
| 556 | try { | ||
| 557 | sf = (StackFrameImpl)frame(0); | ||
| 558 | } catch (IndexOutOfBoundsException exc) { | ||
| 559 | throw new InvalidStackFrameException("No more frames on the stack"); | ||
| 560 | } | ||
| 561 | sf.validateStackFrame(); | ||
| 562 | MethodImpl meth = (MethodImpl)sf.location().method(); | ||
| 563 | ValueImpl convertedValue = ValueImpl.prepareForAssignment(returnValue, | ||
| 564 | meth.getReturnValueContainer()); | ||
| 565 | |||
| 566 | try { | ||
| 567 | JDWP.ThreadReference.ForceEarlyReturn.process(vm, this, convertedValue); | ||
| 568 | } catch (JDWPException exc) { | ||
| 569 | switch (exc.errorCode()) { | ||
| 570 | case JDWP.Error.OPAQUE_FRAME: | ||
| 571 | throw new NativeMethodException(); | ||
| 572 | case JDWP.Error.THREAD_NOT_SUSPENDED: | ||
| 573 | throw new IncompatibleThreadStateException( | ||
| 574 | "Thread not suspended"); | ||
| 575 | case JDWP.Error.THREAD_NOT_ALIVE: | ||
| 576 | throw new IncompatibleThreadStateException( | ||
| 577 | "Thread has not started or has finished"); | ||
| 578 | case JDWP.Error.NO_MORE_FRAMES: | ||
| 579 | throw new InvalidStackFrameException( | ||
| 580 | "No more frames on the stack"); | ||
| 581 | default: | ||
| 582 | throw exc.toJDIException(); | ||
| 583 | } | ||
| 584 | } | ||
| 585 | } | ||
| 586 | |||
| 587 | public String toString() { | ||
| 588 | return "instance of " + referenceType().name() + | ||
| 589 | "(name='" + name() + "', " + "id=" + uniqueID() + ")"; | ||
| 590 | } | ||
| 591 | |||
| 592 | byte typeValueKey() { | ||
| 593 | return JDWP.Tag.THREAD; | ||
| 594 | } | ||
| 595 | |||
| 596 | void addListener(ThreadListener listener) { | ||
| 597 | synchronized (vm.state()) { | ||
| 598 | listeners.add(new WeakReference<ThreadListener>(listener)); | ||
| 599 | } | ||
| 600 | } | ||
| 601 | |||
| 602 | void removeListener(ThreadListener listener) { | ||
| 603 | synchronized (vm.state()) { | ||
| 604 | Iterator iter = listeners.iterator(); | ||
| 605 | while (iter.hasNext()) { | ||
| 606 | WeakReference ref = (WeakReference)iter.next(); | ||
| 607 | if (listener.equals(ref.get())) { | ||
| 608 | iter.remove(); | ||
| 609 | break; | ||
| 610 | } | ||
| 611 | } | ||
| 612 | } | ||
| 613 | } | ||
| 614 | |||
| 615 | /** | ||
| 616 | * Propagate the the thread state change information | ||
| 617 | * to registered listeners. | ||
| 618 | * Must be entered while synchronized on vm.state() | ||
| 619 | */ | ||
| 620 | private void processThreadAction(ThreadAction action) { | ||
| 621 | synchronized (vm.state()) { | ||
| 622 | Iterator iter = listeners.iterator(); | ||
| 623 | while (iter.hasNext()) { | ||
| 624 | WeakReference ref = (WeakReference)iter.next(); | ||
| 625 | ThreadListener listener = (ThreadListener)ref.get(); | ||
| 626 | if (listener != null) { | ||
| 627 | switch (action.id()) { | ||
| 628 | case ThreadAction.THREAD_RESUMABLE: | ||
| 629 | if (!listener.threadResumable(action)) { | ||
| 630 | iter.remove(); | ||
| 631 | } | ||
| 632 | break; | ||
| 633 | } | ||
| 634 | } else { | ||
| 635 | // Listener is unreachable; clean up | ||
| 636 | iter.remove(); | ||
| 637 | } | ||
| 638 | } | ||
| 639 | |||
| 640 | // Discard our local cache | ||
| 641 | resetLocalCache(); | ||
| 642 | } | ||
| 643 | } | ||
| 644 | } | ||