Skip to content

Commit 7fe45c4

Browse files
committed
More proper evaluation aborting.
1 parent a1f8fe5 commit 7fe45c4

File tree

3 files changed

+51
-58
lines changed

3 files changed

+51
-58
lines changed

Mono.Debugging.Soft/SoftDebuggerAdaptor.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
using System.Reflection.Emit;
3333
using System.Collections.Generic;
3434
using System.Runtime.CompilerServices;
35-
using System.Threading;
3635
using System.Threading.Tasks;
3736
using Mono.Debugger.Soft;
3837
using Mono.Debugging.Backend;
@@ -2223,11 +2222,7 @@ public override string Description {
22232222
}
22242223
}
22252224

2226-
protected override void AfterCancelledImpl (int elapsedAfterCancelMs)
2227-
{
2228-
}
2229-
2230-
protected override Task<OperationResult<Value>> InvokeAsyncImpl (CancellationToken token)
2225+
protected override Task<OperationResult<Value>> InvokeAsyncImpl ()
22312226
{
22322227
try {
22332228
var optionsToInvoke = options;
@@ -2236,14 +2231,15 @@ protected override Task<OperationResult<Value>> InvokeAsyncImpl (CancellationTok
22362231
}
22372232
var tcs = new TaskCompletionSource<OperationResult<Value>> ();
22382233
invokeAsyncResult = (IInvokeAsyncResult)obj.BeginInvokeMethod (ctx.Thread, function, args, optionsToInvoke, ar => {
2234+
if (Token.IsCancellationRequested) {
2235+
tcs.SetCanceled ();
2236+
return;
2237+
}
22392238
try {
22402239
var endInvokeResult = obj.EndInvokeMethodWithResult (ar);
2241-
token.ThrowIfCancellationRequested ();
22422240
tcs.SetResult (new SoftOperationResult (endInvokeResult.Result, false, endInvokeResult.OutArgs));
22432241
}
22442242
catch (InvocationException ex) {
2245-
// throw OCE if cancelled
2246-
token.ThrowIfCancellationRequested ();
22472243
if (ex.Exception != null) {
22482244
tcs.SetResult (new SoftOperationResult (ex.Exception, true, null));
22492245
}
@@ -2254,7 +2250,6 @@ protected override Task<OperationResult<Value>> InvokeAsyncImpl (CancellationTok
22542250
catch (CommandException e) {
22552251
if (e.ErrorCode == ErrorCode.INVOKE_ABORTED) {
22562252
tcs.TrySetCanceled ();
2257-
token.ThrowIfCancellationRequested ();
22582253
}
22592254
else {
22602255
tcs.SetException (new EvaluatorException (e.Message));
@@ -2311,7 +2306,7 @@ void UpdateSessionState ()
23112306
ctx.Session.StackVersion++;
23122307
}
23132308

2314-
protected override void CancelImpl ()
2309+
protected override void AbortImpl (int abortCallTimes)
23152310
{
23162311
if (invokeAsyncResult == null) {
23172312
DebuggerLoggingService.LogError ("invokeAsyncResult is null", new ArgumentNullException ("invokeAsyncResult"));

Mono.Debugging/Mono.Debugging.Evaluation/AsyncOperationBase.cs

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public interface IAsyncOperationBase
3232
{
3333
Task RawTask { get; }
3434
string Description { get; }
35-
void AfterCancelled (int elapsedAfterCancelMs);
35+
void Abort ();
3636
}
3737

3838
public abstract class AsyncOperationBase<TValue> : IAsyncOperationBase
@@ -49,40 +49,46 @@ public Task RawTask
4949

5050
public abstract string Description { get; }
5151

52-
public void AfterCancelled (int elapsedAfterCancelMs)
52+
int abortCalls = 0;
53+
54+
readonly CancellationTokenSource tokenSource = new CancellationTokenSource ();
55+
56+
/// <summary>
57+
/// When evaluation is aborted and debugger callback is invoked the implementation has to check
58+
/// for Token.IsCancellationRequested and call Task.SetCancelled() instead of setting the result
59+
/// </summary>
60+
protected CancellationToken Token { get { return tokenSource.Token; } }
61+
62+
public void Abort ()
5363
{
5464
try {
55-
AfterCancelledImpl (elapsedAfterCancelMs);
65+
tokenSource.Cancel();
66+
AbortImpl (Interlocked.Increment (ref abortCalls) - 1);
67+
}
68+
catch (OperationCanceledException) {
69+
// if CancelImpl throw OCE we shouldn't mute it
70+
throw;
5671
}
5772
catch (Exception e) {
58-
DebuggerLoggingService.LogError ("AfterCancelledImpl() thrown an exception", e);
73+
DebuggerLoggingService.LogMessage ("Exception in CancelImpl(): {0}", e.Message);
5974
}
6075
}
6176

62-
protected abstract void AfterCancelledImpl (int elapsedAfterCancelMs);
63-
64-
public Task<OperationResult<TValue>> InvokeAsync (CancellationToken token)
77+
public Task<OperationResult<TValue>> InvokeAsync ()
6578
{
6679
if (Task != null) throw new Exception("Task must be null");
67-
68-
token.Register (() => {
69-
try {
70-
CancelImpl ();
71-
}
72-
catch (OperationCanceledException) {
73-
// if CancelImpl throw OCE we shouldn't mute it
74-
throw;
75-
}
76-
catch (Exception e) {
77-
DebuggerLoggingService.LogMessage ("Exception in CancelImpl(): {0}", e.Message);
78-
}
79-
});
80-
Task = InvokeAsyncImpl (token);
80+
Task = InvokeAsyncImpl ();
8181
return Task;
8282
}
83-
protected abstract Task<OperationResult<TValue>> InvokeAsyncImpl (CancellationToken token);
8483

85-
protected abstract void CancelImpl ();
84+
protected abstract Task<OperationResult<TValue>> InvokeAsyncImpl ();
85+
86+
/// <summary>
87+
/// The implementation has to tell the debugger to abort the evaluation. This method must bot block.
88+
/// </summary>
89+
/// <param name="abortCallTimes">indicates how many times this method has been already called for this evaluation.
90+
/// E.g. the implementation can perform some 'rude abort' after several previous ordinary 'aborts' were failed. For the first call this parameter == 0</param>
91+
protected abstract void AbortImpl (int abortCallTimes);
8692

8793
}
8894
}

Mono.Debugging/Mono.Debugging.Evaluation/AsyncOperationManager.cs

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
using System;
2929
using System.Collections.Generic;
3030
using System.Linq;
31-
using System.Threading;
3231
using System.Threading.Tasks;
3332
using Mono.Debugging.Client;
3433

@@ -39,12 +38,9 @@ public class AsyncOperationManager : IDisposable
3938
class OperationData
4039
{
4140
public IAsyncOperationBase Operation { get; private set; }
42-
public CancellationTokenSource TokenSource { get; private set; }
43-
44-
public OperationData (IAsyncOperationBase operation, CancellationTokenSource tokenSource)
41+
public OperationData (IAsyncOperationBase operation)
4542
{
4643
Operation = operation;
47-
TokenSource = tokenSource;
4844
}
4945
}
5046

@@ -74,13 +70,12 @@ public OperationResult<TValue> Invoke<TValue> (AsyncOperationBase<TValue> mc, in
7470

7571
Task<OperationResult<TValue>> task;
7672
var description = mc.Description;
77-
var cts = new CancellationTokenSource ();
78-
var operationData = new OperationData (mc, cts);
73+
var operationData = new OperationData (mc);
7974
lock (currentOperations) {
8075
if (disposed)
8176
throw new ObjectDisposedException ("Already disposed");
8277
DebuggerLoggingService.LogMessage (string.Format("Starting invoke for {0}", description));
83-
task = mc.InvokeAsync (cts.Token);
78+
task = mc.InvokeAsync ();
8479
currentOperations.Add (operationData);
8580
}
8681

@@ -91,7 +86,7 @@ public OperationResult<TValue> Invoke<TValue> (AsyncOperationBase<TValue> mc, in
9186
return task.Result;
9287
}
9388
DebuggerLoggingService.LogMessage (string.Format ("Invoke {0} timed out after {1} ms. Cancelling.", description, timeout));
94-
cts.Cancel ();
89+
mc.Abort ();
9590
try {
9691
WaitAfterCancel (mc);
9792
}
@@ -127,7 +122,7 @@ public OperationResult<TValue> Invoke<TValue> (AsyncOperationBase<TValue> mc, in
127122
void ChangeBusyState (bool busy, string description)
128123
{
129124
try {
130-
BusyStateChanged (this, new BusyStateEventArgs {IsBusy = true, Description = description});
125+
BusyStateChanged (this, new BusyStateEventArgs {IsBusy = busy, Description = description});
131126
}
132127
catch (Exception e) {
133128
DebuggerLoggingService.LogError ("Exception during ChangeBusyState", e);
@@ -138,24 +133,21 @@ void WaitAfterCancel (IAsyncOperationBase op)
138133
{
139134
var desc = op.Description;
140135
DebuggerLoggingService.LogMessage (string.Format ("Waiting for cancel of invoke {0}", desc));
141-
try {
142-
if (!op.RawTask.Wait (ShortCancelTimeout)) {
143-
try {
144-
ChangeBusyState (true, desc);
145-
op.RawTask.Wait (Timeout.Infinite);
146-
}
147-
finally {
148-
ChangeBusyState (false, desc);
136+
if (!op.RawTask.Wait (ShortCancelTimeout)) {
137+
try {
138+
ChangeBusyState (true, desc);
139+
while (true) {
140+
op.Abort ();
141+
if (op.RawTask.Wait (ShortCancelTimeout))
142+
break;
149143
}
150144
}
151-
}
152-
finally {
153-
DebuggerLoggingService.LogMessage (string.Format ("Calling AfterCancelled() for {0}", desc));
154-
op.AfterCancelled (ShortCancelTimeout);
145+
finally {
146+
ChangeBusyState (false, desc);
147+
}
155148
}
156149
}
157150

158-
159151
public void AbortAll ()
160152
{
161153
DebuggerLoggingService.LogMessage ("Aborting all the current invocations");
@@ -174,7 +166,7 @@ void CancelOperations (List<OperationData> operations, bool wait)
174166
foreach (var operationData in operations) {
175167
var taskDescription = operationData.Operation.Description;
176168
try {
177-
operationData.TokenSource.Cancel ();
169+
operationData.Operation.Abort ();
178170
if (wait) {
179171
WaitAfterCancel (operationData.Operation);
180172
}

0 commit comments

Comments
 (0)