|
1 | 1 | using System.Threading;
|
| 2 | +using System.Threading.Tasks; |
| 3 | +using Microsoft.Samples.Debugging.CorDebug; |
| 4 | +using Microsoft.Samples.Debugging.CorDebug.NativeApi; |
2 | 5 | using Mono.Debugging.Evaluation;
|
3 | 6 |
|
4 | 7 | namespace Mono.Debugging.Win32
|
5 | 8 | {
|
6 |
| - class CorMethodCall: AsyncOperation |
| 9 | + class CorMethodCall: AsyncOperationBase<CorValue> |
7 | 10 | {
|
8 |
| - public delegate void CallCallback ( ); |
9 |
| - public delegate string DescriptionCallback ( ); |
| 11 | + readonly CorEvaluationContext context; |
| 12 | + readonly CorFunction function; |
| 13 | + readonly CorType[] typeArgs; |
| 14 | + readonly CorValue[] args; |
10 | 15 |
|
11 |
| - public CallCallback OnInvoke; |
12 |
| - public CallCallback OnAbort; |
13 |
| - public DescriptionCallback OnGetDescription; |
| 16 | + readonly CorEval eval; |
14 | 17 |
|
15 |
| - public ManualResetEvent DoneEvent = new ManualResetEvent (false); |
| 18 | + public CorMethodCall (CorEvaluationContext context, CorFunction function, CorType[] typeArgs, CorValue[] args) |
| 19 | + { |
| 20 | + this.context = context; |
| 21 | + this.function = function; |
| 22 | + this.typeArgs = typeArgs; |
| 23 | + this.args = args; |
| 24 | + eval = context.Eval; |
| 25 | + } |
16 | 26 |
|
17 |
| - public override string Description |
| 27 | + void ProcessOnEvalComplete (object sender, CorEvalEventArgs evalArgs) |
| 28 | + { |
| 29 | + DoProcessEvalFinished (evalArgs, false); |
| 30 | + } |
| 31 | + |
| 32 | + void ProcessOnEvalException (object sender, CorEvalEventArgs evalArgs) |
18 | 33 | {
|
19 |
| - get { return OnGetDescription (); } |
| 34 | + DoProcessEvalFinished (evalArgs, true); |
20 | 35 | }
|
21 | 36 |
|
22 |
| - public override void Invoke ( ) |
| 37 | + void DoProcessEvalFinished (CorEvalEventArgs evalArgs, bool isException) |
23 | 38 | {
|
24 |
| - OnInvoke (); |
| 39 | + if (evalArgs.Eval != eval) |
| 40 | + return; |
| 41 | + context.Session.OnEndEvaluating (); |
| 42 | + evalArgs.Continue = false; |
| 43 | + tcs.TrySetResult(new OperationResult<CorValue> (evalArgs.Eval.Result, isException)); |
25 | 44 | }
|
26 | 45 |
|
27 |
| - public override void Abort ( ) |
| 46 | + void SubscribeOnEvals () |
28 | 47 | {
|
29 |
| - OnAbort (); |
| 48 | + context.Session.Process.OnEvalComplete += ProcessOnEvalComplete; |
| 49 | + context.Session.Process.OnEvalException += ProcessOnEvalException; |
30 | 50 | }
|
31 | 51 |
|
32 |
| - public override void Shutdown ( ) |
| 52 | + void UnSubcribeOnEvals () |
| 53 | + { |
| 54 | + context.Session.Process.OnEvalComplete -= ProcessOnEvalComplete; |
| 55 | + context.Session.Process.OnEvalException -= ProcessOnEvalException; |
| 56 | + } |
| 57 | + |
| 58 | + public override string Description |
33 | 59 | {
|
34 |
| - try { |
35 |
| - Abort (); |
| 60 | + get |
| 61 | + { |
| 62 | + var met = function.GetMethodInfo (context.Session); |
| 63 | + if (met == null) |
| 64 | + return "<Unknown>"; |
| 65 | + if (met.DeclaringType == null) |
| 66 | + return met.Name; |
| 67 | + return met.DeclaringType.FullName + "." + met.Name; |
36 | 68 | }
|
37 |
| - catch { |
| 69 | + } |
| 70 | + |
| 71 | + readonly TaskCompletionSource<OperationResult<CorValue>> tcs = new TaskCompletionSource<OperationResult<CorValue>> (); |
| 72 | + const int DelayAfterAbort = 500; |
| 73 | + |
| 74 | + protected override void AfterCancelledImpl (int elapsedAfterCancelMs) |
| 75 | + { |
| 76 | + if (tcs.TrySetCanceled ()) { |
| 77 | + // really cancelled for the first time not before. so we should check that we awaited necessary amout of time after Abort() call |
| 78 | + // else if we return too earle after Abort() the process may be PROCESS_NOT_SYNCHRONIZED |
| 79 | + if (elapsedAfterCancelMs < DelayAfterAbort) { |
| 80 | + Thread.Sleep (DelayAfterAbort - elapsedAfterCancelMs); |
| 81 | + } |
38 | 82 | }
|
39 |
| - DoneEvent.Set (); |
| 83 | + context.Session.OnEndEvaluating (); |
40 | 84 | }
|
41 | 85 |
|
42 |
| - public override bool WaitForCompleted (int timeout) |
| 86 | + protected override Task<OperationResult<CorValue>> InvokeAsyncImpl (CancellationToken token) |
| 87 | + { |
| 88 | + SubscribeOnEvals (); |
| 89 | + |
| 90 | + if (function.GetMethodInfo (context.Session).Name == ".ctor") |
| 91 | + eval.NewParameterizedObject (function, typeArgs, args); |
| 92 | + else |
| 93 | + eval.CallParameterizedFunction (function, typeArgs, args); |
| 94 | + context.Session.Process.SetAllThreadsDebugState (CorDebugThreadState.THREAD_SUSPEND, context.Thread); |
| 95 | + context.Session.ClearEvalStatus (); |
| 96 | + context.Session.OnStartEvaluating (); |
| 97 | + context.Session.Process.Continue (false); |
| 98 | + Task = tcs.Task; |
| 99 | + // Don't pass token here, because it causes immediately task cancellation which must be performed by debugger event or real timeout |
| 100 | + // ReSharper disable once MethodSupportsCancellation |
| 101 | + return Task.ContinueWith (task => { |
| 102 | + UnSubcribeOnEvals (); |
| 103 | + return task.Result; |
| 104 | + }); |
| 105 | + } |
| 106 | + |
| 107 | + |
| 108 | + protected override void CancelImpl ( ) |
43 | 109 | {
|
44 |
| - return DoneEvent.WaitOne (timeout, false); |
| 110 | + eval.Abort (); |
45 | 111 | }
|
46 | 112 | }
|
47 | 113 | }
|
0 commit comments