JDK-6700889 : Thread resume invalidates all stack frames, even from other threads
  • Type: Bug
  • Component: core-svc
  • Sub-Component: debugger
  • Affected Version: 5.0
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2008-05-12
  • Updated: 2012-10-01
  • Resolved: 2011-03-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7 Other
6u10Fixed 7 b32Fixed OpenJDK6Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
When a thread is resumed, the call stack frames are invalidated.
The problem is, however, that not only frames of the resumed thread, but also frames of all other suspended threads are invalidated.
This bug is reproduced by the following new JTREG test:


EVALUATION http://hg.openjdk.java.net/jdk6/jdk6/jdk/rev/dd7969318d7a

SUGGESTED FIX See attached webrev.

SUGGESTED FIX See evaluation.

EVALUATION While a thread is suspended, some JDI objects can be created which contain info about the suspended state of the thread. Currently, these objects are StackFrameImpl and MonitorInfoImpl. When the thread is resumed, the info in these objects can no longer be considered to be valid. Methods which access these objects must throw InvalidStackFrameException if called when the thread is not suspended. When these objects are created, the associated ThreadReference is passed to their ctors. The ctors register themselves as ThreadListeners with the associated ThreadReference object. When a ThreadReference.resume is performed, ThreadReferenceImpl notifies its listeners that the thread is resuming. Each listener marks itself as invalid and removes itself as a listener. Subsequent calls to an object marked as invalid cause the InvalidStackFrameException to be thrown. However, what happens when a VM-wide resume occurs, eg, VirtualMachine.resume? In this case, ThreadReference.resume is not called so how do the ThreadReference listeners get notified that their thread is resuming? Read on ... The above talked about info about a particular suspended thread that is contained in StackFrameImpl and MonitorInfoImpl objects and is only valid while the thread is suspended. But other objects can hold info that is only valid while ALL threads are suspended. Most (if not all) of this info is info that is cached in a JDI object so it can be accessed without having to get it from JDWP for each access. An example is that ThreadGroupReferenceImpl caches info about its child threads and threadgroups. It can only do this caching while all threads are suspended, otherwise, a new child could be added to the thread group by a running thread. Thus, ThreadGroupReferenceImpl and other objects that do caching must be notified when VM-wide suspend occurs, and when ANY thread is resumed. To do this, each VirtualMachineImpl has an associated VMState object. Methods in this object are called to 'freeze' and 'thaw' the target VM. While frozen, info can be cached. When a thaw occurs, caches must be discarded and no new caching may be performed until the next freeze. Objects which are affected by the VMState register themselves as VMListeners with the VMState object and are notified when freezes and thaws occur. So, we have two types of listeners and two actions they listen for: - ThreadListeners listen for an individual thread being resumed - VMListeners listen for a VM-wide suspension, and ANY thread being resumed. So we have the questions: a) if a Thread.resume is done, how are VMListeners informed? b) if a VirtualMachine.resume is done, how are ThreadListeners informed? For a), VirtualMachineImpl registers itself as a ThreadListener for each ThreadReferenceImpl that is created. When VirtualMachineImpl is notified that a Thread.resume has occured, it calls VMState.thaw(). For b), when a ThreadReferenceImpl is created, it adds itself as a VMListener to the VMState. Thus, when a thaw occurs, each ThreadReferenceImpl object is notified and it in turn notifies each of its ThreadListeners. However, this has a flaw. Suppose threads T1 and T2 are suspended. Now suppose T2 is resumed by ThreadReference.resume. The following will happen: - ThreadReferenceImpl for T2 will notify each of its ThreadListeners that T2 is resuming - Associated StackFrameImpls and MonitorImpls will invalidate themselves - VMState.thaw will be called - VMState will notify Each VMListener of the thaw and each listener will purge their caches - One of these listeners is the ThreadReferenceImpl for T1. It then notifies each of its listeners of a resume, EVEN THOUGH T1 IS NOT RESUMING. (Note that this means that VMState.thaw will be called recursively, but it is coded to handle that.) Another flaw is that ThreadReferenceImpl itself caches info about the suspended state of the thread, eg, framecount, frames, ... As it happens, MOST of this info is valid as long as the thread is suspended, even if other threads are running. Thus, this cached info doesn't have to be wiped out when some other thread resumes, but it currently is. This doesn't cause a failure, it is just wasted time to reload the cache if another method call comes into the suspended thread. The one exception to this is the thread's name. This can be changed by another thread. --- So, what to fix? - In VirtualMachineImpl's ThreadListener method, threadResumable(ThreadAction), get the thread from the ThreadAction and pass it to thaw. thaw will put it into the VMState object that it creates and which is passed to the vmNotSuspended methods in the listeners. - In ThreadReferenceImpl's VMState vmNotSuspended listener, only notify the thread's listeners if ALL threads are being resumed, based on the contents of the new thread field in VMAction. - Create a new cache in ThreadReferenceImpl that is not wiped out when a different thread is resumed. Move all the cached fields into that cache except for 'name' which continues to reside in the ObjectReferenceImpl subcache, that IS wiped out when any thread resumes. NOTE that ThreadReference.isSuspended() can be called on a thread that isn't suspended. In this case, we can't cache the 'status' value because when the thread really is suspended, this status value is no longer valid. And we don't reset the cache when a thread is suspended, just when it is resumed. - Associated bug fixed is an oversight in the original code that forgot to add MonitorInfoImpl as a ThreadListener.

EVALUATION com.sun.tools.jdi.StackFrameImpl.threadResumable() is called with the ThreadAction, but it does not check which thread was actually resumed. Thus all frames get invalidated regardless on which thread was resumed. *** (#1 of 1): [ UNSAVED ] ###@###.###