How to save exception context in async console application using AsyncPump?

I use the Steven Toub class AsyncPump , which allows console applications to use the async / wait keywords.

However, I have a problem where exceptions that are thrown into the code are caught by the pump and then returned, which causes the original call stack and the exclusive context to get lost.

Here is my test code:

class Program
{
  static void Main(string[] arg)
  {
    AsyncPump.Run(() => MainAsync());
  }

  static async Task MainAsync()
  {
    throw new Exception(); // code should break here
  }
}

If you run this test, the debugger is not broken into throw new Exception()as desired. Instead, it breaks into t.GetAwaiter().GetResult(), which is part of the AsyncPump class itself. This makes it very difficult to debug applications.

Is there a way to reconstruct exceptions so that the debugger breaks in its original location while maintaining the call stack and context?

+3
2

, , async void MainAsync, async Task. , (async void ), , .

async Task . , Task ( ) , task.Result, task.Wait(), await task task.GetAwaiter().GetResult().

: TAP .

AsyncPump, , (.. , ), TaskScheduler.Current TaskScheduler.FromCurrentSynchronizationContext():

/// <summary>
/// PumpingSyncContext, based on AsyncPump
/// http://blogs.msdn.com/b/pfxteam/archive/2012/02/02/await-synchronizationcontext-and-console-apps-part-3.aspx
/// </summary>
class PumpingSyncContext : SynchronizationContext
{
    BlockingCollection<Action> _actions;
    int _pendingOps = 0;

    public TResult Run<TResult>(Func<Task<TResult>> taskFunc, CancellationToken token = default(CancellationToken))
    {
        _actions = new BlockingCollection<Action>();
        SynchronizationContext.SetSynchronizationContext(this);
        try
        {
            var scheduler = TaskScheduler.FromCurrentSynchronizationContext();

            var task = Task.Factory.StartNew(
                async () =>
                {
                    OperationStarted();
                    try
                    {
                        return await taskFunc();
                    }
                    finally
                    {
                        OperationCompleted();
                    }
                },
                token, TaskCreationOptions.None, scheduler).Unwrap();

            // pumping loop
            foreach (var action in _actions.GetConsumingEnumerable())
                action();

            return task.GetAwaiter().GetResult();
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(null);
        }
    }

    void Complete()
    {
        _actions.CompleteAdding();
    }

    // SynchronizationContext methods
    public override SynchronizationContext CreateCopy()
    {
        return this;
    }

    public override void OperationStarted()
    {
        // called when async void method is invoked 
        Interlocked.Increment(ref _pendingOps);
    }

    public override void OperationCompleted()
    {
        // called when async void method completes 
        if (Interlocked.Decrement(ref _pendingOps) == 0)
            Complete();
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        _actions.Add(() => d(state));
    }

    public override void Send(SendOrPostCallback d, object state)
    {
        throw new NotImplementedException("Send");
    }
}

:

return task.GetAwaiter().GetResult();

:

return task.Result;

AggregateException, AggregateException.InnerException async.

+2

GetAwaiter().GetResult() ( , .NET 4.5). .

, , - , , AFAIK VS , . , UserVoice.

, .

+2

Source: https://habr.com/ru/post/1537578/


All Articles