Skip to content

Commit

Permalink
Async operations rewritten to Tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
nerzhulart committed Jan 16, 2017
1 parent d4492b0 commit 3e78933
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 287 deletions.
165 changes: 59 additions & 106 deletions Mono.Debugging.Soft/SoftDebuggerAdaptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;

using System.Threading.Tasks;
using Mono.Debugger.Soft;
using Mono.Debugging.Backend;
using Mono.Debugging.Evaluation;
Expand Down Expand Up @@ -2177,30 +2177,36 @@ static string MirrorStringToString (EvaluationContext ctx, StringMirror mirror)
}
}

class MethodCall: AsyncOperation
internal class SoftOperationResult : OperationResult<Value>
{
readonly InvokeOptions options = InvokeOptions.DisableBreakpoints | InvokeOptions.SingleThreaded;
public SoftOperationResult (Value result, bool resultIsException, Value[] outArgs) : base (result, resultIsException)
{
OutArgs = outArgs;
}

public Value[] OutArgs { get; private set; }
}

readonly ManualResetEvent shutdownEvent = new ManualResetEvent (false);
internal class SoftMethodCall: AsyncOperationBase<Value>
{
readonly InvokeOptions options = InvokeOptions.DisableBreakpoints | InvokeOptions.SingleThreaded;
readonly SoftEvaluationContext ctx;
readonly MethodMirror function;
readonly Value[] args;
readonly object obj;
IAsyncResult handle;
Exception exception;
IInvokeResult result;

public MethodCall (SoftEvaluationContext ctx, MethodMirror function, object obj, Value[] args, bool enableOutArgs)
readonly IInvocableMethodOwnerMirror obj;
IInvokeAsyncResult invokeAsyncResult;

public SoftMethodCall (SoftEvaluationContext ctx, MethodMirror function, IInvocableMethodOwnerMirror obj, Value[] args, bool enableOutArgs)
{
this.ctx = ctx;
this.function = function;
this.obj = obj;
this.args = args;
if (enableOutArgs) {
this.options |= InvokeOptions.ReturnOutArgs;
options |= InvokeOptions.ReturnOutArgs;
}
if (function.VirtualMachine.Version.AtLeast (2, 40)) {
this.options |= InvokeOptions.Virtual;
options |= InvokeOptions.Virtual;
}
}

Expand All @@ -2210,113 +2216,60 @@ public override string Description {
}
}

public override void Invoke ()
protected override void AfterCancelledImpl (int elapsedAfterCancelMs)
{
try {
var invocableMirror = obj as IInvocableMethodOwnerMirror;
if (invocableMirror != null) {
var optionsToInvoke = options;
if (obj is StructMirror) {
optionsToInvoke |= InvokeOptions.ReturnOutThis;
}
handle = invocableMirror.BeginInvokeMethod (ctx.Thread, function, args, optionsToInvoke, null, null);
}
else
throw new ArgumentException ("Soft debugger method calls cannot be invoked on objects of type " + obj.GetType ().Name);
} catch (InvocationException ex) {
ctx.Session.StackVersion++;
exception = ex;
} catch (Exception ex) {
ctx.Session.StackVersion++;
DebuggerLoggingService.LogError ("Error in soft debugger method call thread on " + GetInfo (), ex);
exception = ex;
}
}

public override void Abort ()
{
if (handle is IInvokeAsyncResult) {
var info = GetInfo ();
DebuggerLoggingService.LogMessage ("Aborting invocation of " + info);
((IInvokeAsyncResult) handle).Abort ();
// Don't wait for the abort to finish. The engine will do it.
} else {
throw new NotSupportedException ();
}
}

public override void Shutdown ()
{
shutdownEvent.Set ();
}

void EndInvoke ()
protected override Task<OperationResult<Value>> InvokeAsyncImpl (CancellationToken token)
{
try {
result = ((IInvocableMethodOwnerMirror) obj).EndInvokeMethodWithResult (handle);
} catch (InvocationException ex) {
if (!Aborting && ex.Exception != null) {
string ename = ctx.Adapter.GetValueTypeName (ctx, ex.Exception);
var vref = ctx.Adapter.GetMember (ctx, null, ex.Exception, "Message");

exception = vref != null ? new Exception (ename + ": " + (string) vref.ObjectValue) : new Exception (ename);
return;
var optionsToInvoke = options;
if (obj is StructMirror) {
optionsToInvoke |= InvokeOptions.ReturnOutThis;
}
exception = ex;
} catch (Exception ex) {
DebuggerLoggingService.LogError ("Error in soft debugger method call thread on " + GetInfo (), ex);
exception = ex;
} finally {
ctx.Session.StackVersion++;
}
}

string GetInfo ()
{
try {
TypeMirror type = null;
if (obj is ObjectMirror)
type = ((ObjectMirror)obj).Type;
else if (obj is TypeMirror)
type = (TypeMirror)obj;
else if (obj is StructMirror)
type = ((StructMirror)obj).Type;
return string.Format ("method {0} on object {1}",
function == null? "[null]" : function.FullName,
type == null? "[null]" : type.FullName);
} catch (Exception ex) {
DebuggerLoggingService.LogError ("Error getting info for SDB MethodCall", ex);
return "";
var tcs = new TaskCompletionSource<OperationResult<Value>> ();
invokeAsyncResult = (IInvokeAsyncResult)obj.BeginInvokeMethod (ctx.Thread, function, args, optionsToInvoke, ar => {
try {
var endInvokeResult = obj.EndInvokeMethodWithResult (ar);
token.ThrowIfCancellationRequested ();
tcs.SetResult (new SoftOperationResult (endInvokeResult.Result, false, endInvokeResult.OutArgs));
}
catch (InvocationException ex) {
// throw OCE if cancelled
token.ThrowIfCancellationRequested ();
if (ex.Exception != null) {
tcs.SetResult (new SoftOperationResult (ex.Exception, true, null));
}
else {
tcs.SetException (new EvaluatorException ("Target method has thrown an exception but the exception object is inaccessible"));
}
}
catch (Exception e) {
tcs.SetException (e);
}
finally {
UpdateSessionState ();
}
}, null);
return tcs.Task;
} catch (Exception) {
UpdateSessionState ();
throw;
}
}

public override bool WaitForCompleted (int timeout)
void UpdateSessionState ()
{
if (handle == null)
return true;
int res = WaitHandle.WaitAny (new WaitHandle[] { handle.AsyncWaitHandle, shutdownEvent }, timeout);
if (res == 0) {
EndInvoke ();
return true;
}
// Return true if shut down.
return res == 1;
ctx.Session.StackVersion++;
}

public Value ReturnValue {
get {
if (exception != null)
throw new EvaluatorException (exception.Message);
return result.Result;
}
}

public Value[] OutArgs {
get {
if (exception != null)
throw new EvaluatorException (exception.Message);
return result.OutArgs;
protected override void CancelImpl ()
{
if (invokeAsyncResult == null) {
DebuggerLoggingService.LogError ("invokeAsyncResult is null", new ArgumentNullException ("invokeAsyncResult"));
return;
}
invokeAsyncResult.Abort ();
}
}
}
15 changes: 9 additions & 6 deletions Mono.Debugging.Soft/SoftEvaluationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,16 +190,19 @@ Value RuntimeInvoke (MethodMirror method, object target, Value[] values, bool en
DC.DebuggerLoggingService.LogMessage ("Thread state before evaluation is {0}", threadState);
throw new EvaluatorException ("Evaluation is not allowed when the thread is in 'Wait' state");
}
var mc = new MethodCall (this, method, target, values, enableOutArgs);
var invocableMirror = target as IInvocableMethodOwnerMirror;
if (invocableMirror == null)
throw new ArgumentException ("Soft debugger method calls cannot be invoked on objects of type " + target.GetType ().Name);
var mc = new SoftMethodCall (this, method, invocableMirror, values, enableOutArgs);
//Since runtime is returning NOT_SUSPENDED error if two methods invokes are executed
//at same time we have to lock invoking to prevent this...
lock (method.VirtualMachine) {
Adapter.AsyncExecute (mc, Options.EvaluationTimeout);
}
if (enableOutArgs) {
outArgs = mc.OutArgs;
var result = (SoftOperationResult)Adapter.InvokeSync (mc, Options.EvaluationTimeout).ThrowIfException (this);
if (enableOutArgs) {
outArgs = result.OutArgs;
}
return result.Result;
}
return mc.ReturnValue;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace Mono.Debugging.Evaluation
/// will then be made asynchronous and the Run method will immediately return an ObjectValue
/// with the Evaluating state.
/// </summary>
public class AsyncEvaluationTracker: RemoteFrameObject, IObjectValueUpdater, IDisposable
public class AsyncEvaluationTracker: IObjectValueUpdater, IDisposable
{
Dictionary<string, UpdateCallback> asyncCallbacks = new Dictionary<string, UpdateCallback> ();
Dictionary<string, ObjectValue> asyncResults = new Dictionary<string, ObjectValue> ();
Expand Down
88 changes: 88 additions & 0 deletions Mono.Debugging/Mono.Debugging.Evaluation/AsyncOperationBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Mono.Debugging.Client;

namespace Mono.Debugging.Evaluation
{
public class OperationResult<TValue>
{
public TValue Result { get; private set; }
public bool ResultIsException { get; private set; }

public OperationResult (TValue result, bool resultIsException)
{
Result = result;
ResultIsException = resultIsException;
}
}

public static class OperationResultEx
{
public static OperationResult<TValue> ThrowIfException<TValue> (this OperationResult<TValue> result, EvaluationContext ctx)
{
if (!result.ResultIsException)
return result;
var exceptionTypeName = ctx.Adapter.GetValueTypeName (ctx, result.Result);
throw new EvaluatorExceptionThrownException (result.Result, exceptionTypeName);
}
}

public interface IAsyncOperationBase
{
Task RawTask { get; }
string Description { get; }
void AfterCancelled (int elapsedAfterCancelMs);
}

public abstract class AsyncOperationBase<TValue> : IAsyncOperationBase
{
public Task<OperationResult<TValue>> Task { get; protected set; }

public Task RawTask
{
get
{
return Task;
}
}

public abstract string Description { get; }

public void AfterCancelled (int elapsedAfterCancelMs)
{
try {
AfterCancelledImpl (elapsedAfterCancelMs);
}
catch (Exception e) {
DebuggerLoggingService.LogError ("AfterCancelledImpl() thrown an exception", e);
}
}

protected abstract void AfterCancelledImpl (int elapsedAfterCancelMs);

public Task<OperationResult<TValue>> InvokeAsync (CancellationToken token)
{
if (Task != null) throw new Exception("Task must be null");

token.Register (() => {
try {
CancelImpl ();
}
catch (OperationCanceledException) {
// if CancelImpl throw OCE we shouldn't mute it
throw;
}
catch (Exception e) {
DebuggerLoggingService.LogMessage ("Exception in CancelImpl(): {0}", e.Message);
}
});
Task = InvokeAsyncImpl (token);
return Task;
}
protected abstract Task<OperationResult<TValue>> InvokeAsyncImpl (CancellationToken token);

protected abstract void CancelImpl ();

}
}
Loading

0 comments on commit 3e78933

Please sign in to comment.