//
public void StartBuffering () {
buffer_packets = true;
- if (Version.AtLeast (3, 34))
+ if (Version.AtLeast (2, 34))
VM_StartBuffering ();
}
public void StopBuffering () {
- if (Version.AtLeast (3, 34))
+ if (Version.AtLeast (2, 34))
VM_StopBuffering ();
buffer_packets = false;
return SendReceive (CommandSet.THREAD, (int)CmdThread.GET_NAME, new PacketWriter ().WriteId (id)).ReadString ();
}
- internal FrameInfo[] Thread_GetFrameInfo (long id, int start_frame, int length) {
- var res = SendReceive (CommandSet.THREAD, (int)CmdThread.GET_FRAME_INFO, new PacketWriter ().WriteId (id).WriteInt (start_frame).WriteInt (length));
- int count = res.ReadInt ();
-
- var frames = new FrameInfo [count];
- for (int i = 0; i < count; ++i) {
- var f = new FrameInfo ();
- f.id = res.ReadInt ();
- f.method = res.ReadId ();
- f.il_offset = res.ReadInt ();
- f.flags = (StackFrameFlags)res.ReadByte ();
- frames [i] = f;
- }
-
- return frames;
+ internal void Thread_GetFrameInfo (long id, int start_frame, int length, Action<FrameInfo[]> resultCallaback) {
+ Send (CommandSet.THREAD, (int)CmdThread.GET_FRAME_INFO, new PacketWriter ().WriteId (id).WriteInt (start_frame).WriteInt (length), (res) => {
+ int count = res.ReadInt ();
+ var frames = new FrameInfo[count];
+ for (int i = 0; i < count; ++i) {
+ var f = new FrameInfo ();
+ f.id = res.ReadInt ();
+ f.method = res.ReadId ();
+ f.il_offset = res.ReadInt ();
+ f.flags = (StackFrameFlags)res.ReadByte ();
+ frames [i] = f;
+ }
+ resultCallaback (frames);
+ }, 1);
}
internal int Thread_GetState (long id) {
f |= InvokeFlags.SINGLE_THREADED;
InvokeAsyncResult r = new InvokeAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent (false), VM = vm, Thread = thread, Callback = callback };
-
+ thread.InvalidateFrames ();
r.ID = vm.conn.VM_BeginInvokeMethod (thread.Id, method.Id, this_obj != null ? vm.EncodeValue (this_obj) : vm.EncodeValue (vm.CreateValue (null)), vm.EncodeValues (arguments), f, InvokeCB, r);
return r;
var args = new List<ValueImpl[]> ();
for (int i = 0; i < methods.Length; ++i)
args.Add (vm.EncodeValues (arguments [i]));
+ thread.InvalidateFrames ();
r.ID = vm.conn.VM_BeginInvokeMethods (thread.Id, mids, this_obj != null ? vm.EncodeValue (this_obj) : vm.EncodeValue (vm.CreateValue (null)), args, f, InvokeMultipleCB, r);
return r;
public class ThreadMirror : ObjectMirror
{
string name;
+ bool framesCacheIsInvalid = true;
+ bool fetchingInProgress;
+ object fetchingLocker = new object ();
+ ManualResetEvent fetchingEvent = new ManualResetEvent (false);
ThreadInfo info;
+ StackFrame[] frames;
internal ThreadMirror (VirtualMachine vm, long id) : base (vm, id) {
}
internal ThreadMirror (VirtualMachine vm, long id, TypeMirror type, AppDomainMirror domain) : base (vm, id, type, domain) {
}
- // FIXME: Cache, invalidate when the thread/runtime is resumed
public StackFrame[] GetFrames () {
- FrameInfo[] frame_info = vm.conn.Thread_GetFrameInfo (id, 0, -1);
+ FetchFrames (true);
+ fetchingEvent.WaitOne ();
+ return frames;
+ }
- var frames = new List<StackFrame> ();
+ internal void InvalidateFrames () {
+ framesCacheIsInvalid = true;
+ }
- for (int i = 0; i < frame_info.Length; ++i) {
- FrameInfo info = (FrameInfo)frame_info [i];
- MethodMirror method = vm.GetMethod (info.method);
- var f = new StackFrame (vm, info.id, this, method, info.il_offset, info.flags);
- if (!(f.IsNativeTransition && !NativeTransitions))
- frames.Add (f);
+ internal void FetchFrames (bool mustFetch = false) {
+ lock (fetchingLocker) {
+ if (fetchingInProgress || !framesCacheIsInvalid)
+ return;
+ framesCacheIsInvalid = false;
+ fetchingInProgress = true;
+ fetchingEvent.Reset ();
}
+ vm.conn.Thread_GetFrameInfo (id, 0, -1, (frame_info) => {
+ var framesList = new List<StackFrame> ();
+ for (int i = 0; i < frame_info.Length; ++i) {
+ var frameInfo = (FrameInfo)frame_info [i];
+ var method = vm.GetMethod (frameInfo.method);
+ var f = new StackFrame (vm, frameInfo.id, this, method, frameInfo.il_offset, frameInfo.flags);
+ if (!(f.IsNativeTransition && !NativeTransitions))
+ framesList.Add (f);
+ }
+ lock (fetchingLocker) {
+ vm.AddThreadToInvalidateList (this);
+ fetchingInProgress = false;
+ //In case it was invalidated during waiting for response from
+ //runtime and mustFetch was set refetch
+ if (framesCacheIsInvalid && mustFetch) {
+ FetchFrames (mustFetch);
+ return;
+ }
+ frames = framesList.ToArray ();
+ fetchingEvent.Set ();
+ }
+ });
+ }
- return frames.ToArray ();
- }
+ public static void FetchFrames(IList<ThreadMirror> threads)
+ {
+ if (threads.Count == 0)
+ return;
+ threads [0].vm.conn.StartBuffering ();
+ foreach (var thread in threads) {
+ thread.FetchFrames ();
+ }
+ threads [0].vm.conn.StopBuffering ();
+ }
public string Name {
get {
name = vm.conn.Thread_GetName (id);
return name;
}
- }
+ }
public new long Id {
get {
public void Resume () {
try {
+ InvalidateThreadsAndFramesCache ();
conn.VM_Resume ();
} catch (CommandException ex) {
if (ex.ErrorCode == ErrorCode.NOT_SUSPENDED)
conn.ForceDisconnect ();
}
+ HashSet<ThreadMirror> threadsToInvalidate = new HashSet<ThreadMirror> ();
+ ThreadMirror[] threadsCache;
+ object threadCacheLocker=new object();
+
+ internal void InvalidateThreadsAndFramesCache () {
+ lock (threadsToInvalidate) {
+ foreach (var thread in threadsToInvalidate)
+ thread.InvalidateFrames ();
+ threadsToInvalidate.Clear ();
+ }
+ lock (threadCacheLocker) {
+ threadsCache = null;
+ }
+ }
+
+ internal void InvalidateThreadsCache () {
+ lock (threadCacheLocker) {
+ threadsCache = null;
+ }
+ }
+
+ internal void AddThreadToInvalidateList (ThreadMirror threadMirror)
+ {
+ lock (threadsToInvalidate) {
+ threadsToInvalidate.Add (threadMirror);
+ }
+ }
+
public IList<ThreadMirror> GetThreads () {
- long[] ids = vm.conn.VM_GetThreads ();
- ThreadMirror[] res = new ThreadMirror [ids.Length];
- for (int i = 0; i < ids.Length; ++i)
- res [i] = GetThread (ids [i]);
- return res;
+ lock (threadCacheLocker) {
+ if (threadsCache == null) {
+ long[] ids = vm.conn.VM_GetThreads ();
+ threadsCache = new ThreadMirror [ids.Length];
+ for (int i = 0; i < ids.Length; ++i)
+ threadsCache [i] = GetThread (ids [i]);
+ }
+ return threadsCache;
+ }
}
// Same as the mirrorOf methods in JDI
vm.notify_vm_event (EventType.VMDeath, suspend_policy, req_id, thread_id, null, ei.ExitCode);
break;
case EventType.ThreadStart:
+ vm.InvalidateThreadsCache ();
l.Add (new ThreadStartEvent (vm, req_id, id));
break;
case EventType.ThreadDeath:
+ vm.InvalidateThreadsCache ();
l.Add (new ThreadDeathEvent (vm, req_id, id));
break;
case EventType.AssemblyLoad: