1
#if CLR_2_0 || CLR_4_0
3
using System.Collections.Generic;
4
using System.Reflection;
5
using System.Runtime.Serialization;
6
using System.Threading;
10
internal abstract class AsyncInvocationRegion : IDisposable
12
private static readonly Type AsyncStateMachineAttribute = Type.GetType("System.Runtime.CompilerServices.AsyncStateMachineAttribute");
14
private AsyncInvocationRegion()
18
public static AsyncInvocationRegion Create(MethodInfo method)
20
if (!IsAsyncOperation(method))
21
throw new InvalidOperationException(@"Either asynchronous support is not available or an attempt
22
at wrapping a non-async method invocation in an async region was done");
24
if (method.ReturnType == typeof(void))
25
return new AsyncVoidInvocationRegion();
27
return new AsyncTaskInvocationRegion();
30
public static AsyncInvocationRegion Create(Delegate @delegate)
32
return Create(@delegate.Method);
35
public static bool IsAsyncOperation(MethodInfo method)
37
return AsyncStateMachineAttribute != null && method.IsDefined(AsyncStateMachineAttribute, false);
40
public static bool IsAsyncOperation(Delegate @delegate)
42
return IsAsyncOperation(@delegate.Method);
46
/// Waits for pending asynchronous operations to complete, if appropriate,
47
/// and returns a proper result of the invocation by unwrapping task results
49
/// <param name="invocationResult">The raw result of the method invocation</param>
50
/// <returns>The unwrapped result, if necessary</returns>
51
/// <exception cref="AsyncInvocationException">If any exception is thrown while waiting for completion</exception>
52
public abstract object WaitForPendingOperationsToComplete(object invocationResult);
54
public virtual void Dispose()
57
private class AsyncVoidInvocationRegion : AsyncInvocationRegion
59
private readonly SynchronizationContext _previousContext;
60
private readonly AsyncSynchronizationContext _currentContext;
62
public AsyncVoidInvocationRegion()
64
_previousContext = SynchronizationContext.Current;
65
_currentContext = new AsyncSynchronizationContext();
66
SynchronizationContext.SetSynchronizationContext(_currentContext);
69
public override void Dispose()
71
SynchronizationContext.SetSynchronizationContext(_previousContext);
74
public override object WaitForPendingOperationsToComplete(object invocationResult)
78
_currentContext.WaitForPendingOperationsToComplete();
79
return invocationResult;
83
throw new AsyncInvocationException(e);
88
private class AsyncTaskInvocationRegion : AsyncInvocationRegion
90
private const string TaskWaitMethod = "Wait";
91
private const string TaskResultProperty = "Result";
92
private const string SystemAggregateException = "System.AggregateException";
93
private const string InnerExceptionsProperty = "InnerExceptions";
94
private const BindingFlags TaskResultPropertyBindingFlags = BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public;
96
public override object WaitForPendingOperationsToComplete(object invocationResult)
100
invocationResult.GetType().GetMethod(TaskWaitMethod, new Type[0]).Invoke(invocationResult, null);
102
catch (TargetInvocationException e)
104
var innerExceptions = GetAllExceptions(e.InnerException);
106
throw new AsyncInvocationException(innerExceptions[0]);
109
var taskResultProperty = invocationResult.GetType().GetProperty(TaskResultProperty, TaskResultPropertyBindingFlags);
111
return taskResultProperty != null ? taskResultProperty.GetValue(invocationResult, null) : invocationResult;
114
private static IList<Exception> GetAllExceptions(Exception exception)
116
if (SystemAggregateException.Equals(exception.GetType().FullName))
117
return (IList<Exception>)exception.GetType().GetProperty(InnerExceptionsProperty).GetValue(exception, null);
119
return new[] { exception };
125
internal class AsyncInvocationException : Exception
127
public AsyncInvocationException(Exception innerException)
128
: base("An exception has occurred during an asynchronous operation", innerException)
132
protected AsyncInvocationException(SerializationInfo info, StreamingContext context)
133
: base(info, context)
b'\\ No newline at end of file'