This repository has been archived by the owner on Oct 4, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CorDebug] Invocations rewritten to .net Task API
- Loading branch information
1 parent
ae4eac9
commit 7a73da3
Showing
2 changed files
with
109 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 86 additions & 20 deletions
106
main/src/addins/MonoDevelop.Debugger.Win32/Mono.Debugging.Win32/CorMethodCall.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,113 @@ | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Samples.Debugging.CorDebug; | ||
using Microsoft.Samples.Debugging.CorDebug.NativeApi; | ||
using Mono.Debugging.Evaluation; | ||
|
||
namespace Mono.Debugging.Win32 | ||
{ | ||
class CorMethodCall: AsyncOperation | ||
class CorMethodCall: AsyncOperationBase<CorValue> | ||
{ | ||
public delegate void CallCallback ( ); | ||
public delegate string DescriptionCallback ( ); | ||
readonly CorEvaluationContext context; | ||
readonly CorFunction function; | ||
readonly CorType[] typeArgs; | ||
readonly CorValue[] args; | ||
|
||
public CallCallback OnInvoke; | ||
public CallCallback OnAbort; | ||
public DescriptionCallback OnGetDescription; | ||
readonly CorEval eval; | ||
|
||
public ManualResetEvent DoneEvent = new ManualResetEvent (false); | ||
public CorMethodCall (CorEvaluationContext context, CorFunction function, CorType[] typeArgs, CorValue[] args) | ||
{ | ||
this.context = context; | ||
this.function = function; | ||
this.typeArgs = typeArgs; | ||
this.args = args; | ||
eval = context.Eval; | ||
} | ||
|
||
public override string Description | ||
void ProcessOnEvalComplete (object sender, CorEvalEventArgs evalArgs) | ||
{ | ||
DoProcessEvalFinished (evalArgs, false); | ||
} | ||
|
||
void ProcessOnEvalException (object sender, CorEvalEventArgs evalArgs) | ||
{ | ||
get { return OnGetDescription (); } | ||
DoProcessEvalFinished (evalArgs, true); | ||
} | ||
|
||
public override void Invoke ( ) | ||
void DoProcessEvalFinished (CorEvalEventArgs evalArgs, bool isException) | ||
{ | ||
OnInvoke (); | ||
if (evalArgs.Eval != eval) | ||
return; | ||
context.Session.OnEndEvaluating (); | ||
evalArgs.Continue = false; | ||
tcs.TrySetResult(new OperationResult<CorValue> (evalArgs.Eval.Result, isException)); | ||
} | ||
|
||
public override void Abort ( ) | ||
void SubscribeOnEvals () | ||
{ | ||
OnAbort (); | ||
context.Session.Process.OnEvalComplete += ProcessOnEvalComplete; | ||
context.Session.Process.OnEvalException += ProcessOnEvalException; | ||
} | ||
|
||
public override void Shutdown ( ) | ||
void UnSubcribeOnEvals () | ||
{ | ||
context.Session.Process.OnEvalComplete -= ProcessOnEvalComplete; | ||
context.Session.Process.OnEvalException -= ProcessOnEvalException; | ||
} | ||
|
||
public override string Description | ||
{ | ||
try { | ||
Abort (); | ||
get | ||
{ | ||
var met = function.GetMethodInfo (context.Session); | ||
if (met == null) | ||
return "<Unknown>"; | ||
if (met.DeclaringType == null) | ||
return met.Name; | ||
return met.DeclaringType.FullName + "." + met.Name; | ||
} | ||
catch { | ||
} | ||
|
||
readonly TaskCompletionSource<OperationResult<CorValue>> tcs = new TaskCompletionSource<OperationResult<CorValue>> (); | ||
const int DelayAfterAbort = 500; | ||
|
||
protected override void AfterCancelledImpl (int elapsedAfterCancelMs) | ||
{ | ||
if (tcs.TrySetCanceled ()) { | ||
// really cancelled for the first time not before. so we should check that we awaited necessary amout of time after Abort() call | ||
// else if we return too earle after Abort() the process may be PROCESS_NOT_SYNCHRONIZED | ||
if (elapsedAfterCancelMs < DelayAfterAbort) { | ||
Thread.Sleep (DelayAfterAbort - elapsedAfterCancelMs); | ||
} | ||
} | ||
DoneEvent.Set (); | ||
context.Session.OnEndEvaluating (); | ||
} | ||
|
||
public override bool WaitForCompleted (int timeout) | ||
protected override Task<OperationResult<CorValue>> InvokeAsyncImpl (CancellationToken token) | ||
{ | ||
SubscribeOnEvals (); | ||
|
||
if (function.GetMethodInfo (context.Session).Name == ".ctor") | ||
eval.NewParameterizedObject (function, typeArgs, args); | ||
else | ||
eval.CallParameterizedFunction (function, typeArgs, args); | ||
context.Session.Process.SetAllThreadsDebugState (CorDebugThreadState.THREAD_SUSPEND, context.Thread); | ||
context.Session.ClearEvalStatus (); | ||
context.Session.OnStartEvaluating (); | ||
context.Session.Process.Continue (false); | ||
Task = tcs.Task; | ||
// Don't pass token here, because it causes immediately task cancellation which must be performed by debugger event or real timeout | ||
// ReSharper disable once MethodSupportsCancellation | ||
return Task.ContinueWith (task => { | ||
UnSubcribeOnEvals (); | ||
return task.Result; | ||
}); | ||
} | ||
|
||
|
||
protected override void CancelImpl ( ) | ||
{ | ||
return DoneEvent.WaitOne (timeout, false); | ||
eval.Abort (); | ||
} | ||
} | ||
} |