Skip to content

Commit d8ef7b8

Browse files
committed
More proper evaluation aborting.
1 parent 1d4a9c4 commit d8ef7b8

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;
@@ -2191,11 +2190,7 @@ public override string Description {
21912190
}
21922191
}
21932192

2194-
protected override void AfterCancelledImpl (int elapsedAfterCancelMs)
2195-
{
2196-
}
2197-
2198-
protected override Task<OperationResult<Value>> InvokeAsyncImpl (CancellationToken token)
2193+
protected override Task<OperationResult<Value>> InvokeAsyncImpl ()
21992194
{
22002195
try {
22012196
var optionsToInvoke = options;
@@ -2204,14 +2199,15 @@ protected override Task<OperationResult<Value>> InvokeAsyncImpl (CancellationTok
22042199
}
22052200
var tcs = new TaskCompletionSource<OperationResult<Value>> ();
22062201
invokeAsyncResult = (IInvokeAsyncResult)obj.BeginInvokeMethod (ctx.Thread, function, args, optionsToInvoke, ar => {
2202+
if (Token.IsCancellationRequested) {
2203+
tcs.SetCanceled ();
2204+
return;
2205+
}
22072206
try {
22082207
var endInvokeResult = obj.EndInvokeMethodWithResult (ar);
2209-
token.ThrowIfCancellationRequested ();
22102208
tcs.SetResult (new SoftOperationResult (endInvokeResult.Result, false, endInvokeResult.OutArgs));
22112209
}
22122210
catch (InvocationException ex) {
2213-
// throw OCE if cancelled
2214-
token.ThrowIfCancellationRequested ();
22152211
if (ex.Exception != null) {
22162212
tcs.SetResult (new SoftOperationResult (ex.Exception, true, null));
22172213
}
@@ -2222,7 +2218,6 @@ protected override Task<OperationResult<Value>> InvokeAsyncImpl (CancellationTok
22222218
catch (CommandException e) {
22232219
if (e.ErrorCode == ErrorCode.INVOKE_ABORTED) {
22242220
tcs.TrySetCanceled ();
2225-
token.ThrowIfCancellationRequested ();
22262221
}
22272222
else {
22282223
tcs.SetException (new EvaluatorException (e.Message));
@@ -2279,7 +2274,7 @@ void UpdateSessionState ()
22792274
ctx.Session.StackVersion++;
22802275
}
22812276

2282-
protected override void CancelImpl ()
2277+
protected override void AbortImpl (int abortCallTimes)
22832278
{
22842279
if (invokeAsyncResult == null) {
22852280
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)