网上演练
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

382 lines
15 KiB

#if NET20 || NET30 || NET35 || NET40 || !NET_4_6
using System.Reflection;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Provides an awaiter for awaiting a <see cref="Task" /> .
/// </summary>
/// <remarks>
/// This type is intended for compiler use only.
/// </remarks>
public struct TaskAwaiter : ICriticalNotifyCompletion
{
/// <summary>
/// A MethodInfo for the Exception.PrepForRemoting method.
/// </summary>
private static readonly MethodInfo _prepForRemoting = GetPrepForRemotingMethodInfo();
/// <summary>
/// An empty array to use with MethodInfo.Invoke.
/// </summary>
private static readonly object[] _emptyParams = new object[0];
/// <summary>
/// The default value to use for continueOnCapturedContext.
/// </summary>
internal const bool continueOnCapturedContextDefaultValue = true;
/// <summary>
/// Error message for GetAwaiter.
/// </summary>
private const string _invalidOperationExceptionTaskNotCompleted = "The task has not yet completed."; // TODO not used
/// <summary>
/// The task being awaited.
/// </summary>
private readonly Task _task;
/// <summary>
/// Gets whether the task being awaited is completed.
/// </summary>
/// <remarks>
/// This property is intended for compiler user rather than use directly in code.
/// </remarks>
/// <exception cref="NullReferenceException">The awaiter was not properly initialized.</exception>
public bool IsCompleted
{
get { return _task.IsCompleted; }
}
/// <summary>
/// Whether the current thread is appropriate for inlining the await continuation.
/// </summary>
private static bool IsValidLocationForInlining
{
get
{
var current = SynchronizationContext.Current;
if (current != null && current.GetType() != typeof(SynchronizationContext))
{
return false;
}
return TaskScheduler.Current == TaskScheduler.Default;
}
}
/// <summary>
/// Initializes the <see cref="TaskAwaiter" /> .
/// </summary>
/// <param name="task"> The <see cref="Task" /> to be awaited. </param>
internal TaskAwaiter(Task task)
{
_task = task;
}
/// <summary>
/// Schedules the continuation onto the <see cref="Task" /> associated with this <see cref="TaskAwaiter" /> .
/// </summary>
/// <param name="continuation"> The action to invoke when the await operation completes. </param>
/// <exception cref="ArgumentNullException">The
/// <paramref name="continuation" />
/// argument is null (Nothing in Visual Basic).</exception>
/// <exception cref="InvalidOperationException">The awaiter was not properly initialized.</exception>
/// <remarks>
/// This method is intended for compiler user rather than use directly in code.
/// </remarks>
public void OnCompleted(Action continuation)
{
OnCompletedInternal(_task, continuation, true);
}
/// <summary>
/// Schedules the continuation onto the <see cref="Task" /> associated with this <see cref="TaskAwaiter" /> .
/// </summary>
/// <param name="continuation"> The action to invoke when the await operation completes. </param>
/// <exception cref="ArgumentNullException">The
/// <paramref name="continuation" />
/// argument is null (Nothing in Visual Basic).</exception>
/// <exception cref="InvalidOperationException">The awaiter was not properly initialized.</exception>
/// <remarks>
/// This method is intended for compiler user rather than use directly in code.
/// </remarks>
[SecurityCritical]
public void UnsafeOnCompleted(Action continuation)
{
OnCompletedInternal(_task, continuation, true);
}
/// <summary>
/// Ends the await on the completed <see cref="Task" /> .
/// </summary>
/// <exception cref="NullReferenceException">The awaiter was not properly initialized.</exception>
/// <exception cref="InvalidOperationException">The task was not yet completed.</exception>
/// <exception cref="TaskCanceledException">The task was canceled.</exception>
/// <exception cref="Exception">The task completed in a Faulted state.</exception>
public void GetResult()
{
ValidateEnd(_task);
}
/// <summary>
/// Fast checks for the end of an await operation to determine whether more needs to be done prior to completing the await.
/// </summary>
/// <param name="task"> The awaited task. </param>
internal static void ValidateEnd(Task task)
{
if (task.Status == TaskStatus.RanToCompletion)
{
return;
}
HandleNonSuccess(task);
}
/// <summary>
/// Handles validations on tasks that aren't successfully completed.
/// </summary>
/// <param name="task"> The awaited task. </param>
private static void HandleNonSuccess(Task task)
{
if (!task.IsCompleted)
{
try
{
task.Wait();
}
catch (Exception ex)
{
GC.KeepAlive(ex);
}
}
if (task.Status == TaskStatus.RanToCompletion)
{
return;
}
ThrowForNonSuccess(task);
}
private static void ThrowForNonSuccess(Task task)
{
switch (task.Status)
{
case TaskStatus.Canceled:
throw new TaskCanceledException(task);
case TaskStatus.Faulted:
throw PrepareExceptionForRethrow(task.Exception.InnerException);
default:
throw new InvalidOperationException("The task has not yet completed.");
}
}
/// <summary>
/// Schedules the continuation onto the <see cref="Task" /> associated with this <see cref="TaskAwaiter" /> .
/// </summary>
/// <param name="task"> The awaited task. </param>
/// <param name="continuation"> The action to invoke when the await operation completes. </param>
/// <param name="continueOnCapturedContext"> Whether to capture and marshal back to the current context. </param>
/// <exception cref="ArgumentNullException">The
/// <paramref name="continuation" />
/// argument is null (Nothing in Visual Basic).</exception>
/// <exception cref="NullReferenceException">The awaiter was not properly initialized.</exception>
/// <remarks>
/// This method is intended for compiler user rather than use directly in code.
/// </remarks>
internal static void OnCompletedInternal(Task task, Action continuation, bool continueOnCapturedContext)
{
if (continuation == null)
{
throw new ArgumentNullException("continuation");
}
var syncContext = continueOnCapturedContext ? SynchronizationContext.Current : null;
if (syncContext != null && syncContext.GetType() != typeof(SynchronizationContext))
{
task.ContinueWith(result =>
{
try
{
syncContext.Post(state => ((Action)state)(), continuation);
}
catch (Exception ex)
{
AsyncMethodBuilderCore.ThrowOnContext(ex, null);
}
}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
else
{
var scheduler = continueOnCapturedContext ? TaskScheduler.Current : TaskScheduler.Default;
if (task.IsCompleted)
{
Task.Factory.StartNew(state => ((Action)state)(), continuation, CancellationToken.None,
TaskCreationOptions.None, scheduler);
}
else if (scheduler != TaskScheduler.Default)
{
task.ContinueWith(_ => RunNoException(continuation), CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, scheduler);
}
else
{
task.ContinueWith(result =>
{
if (IsValidLocationForInlining)
{
RunNoException(continuation);
}
else
{
Task.Factory.StartNew(state => RunNoException((Action)state), continuation,
CancellationToken.None, TaskCreationOptions.None,
TaskScheduler.Default);
}
}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
}
}
private static void RunNoException(Action continuation)
{
if (continuation == null)
{
return;
}
try
{
continuation();
}
catch (Exception ex)
{
AsyncMethodBuilderCore.ThrowOnContext(ex, null);
}
}
/// <summary>
/// Copies the exception's stack trace so its stack trace isn't overwritten.
/// </summary>
/// <param name="exc"> The exception to prepare. </param>
internal static Exception PrepareExceptionForRethrow(Exception exc)
{
if (_prepForRemoting != null)
{
try
{
_prepForRemoting.Invoke(exc, _emptyParams);
}
catch (Exception ex)
{
GC.KeepAlive(ex);
}
}
return exc;
}
/// <summary>
/// Gets the MethodInfo for the internal PrepForRemoting method on Exception.
/// </summary>
/// <returns> The MethodInfo if it could be retrieved, or else null. </returns>
private static MethodInfo GetPrepForRemotingMethodInfo()
{
try
{
return typeof(Exception).GetMethod("PrepForRemoting", BindingFlags.Instance | BindingFlags.NonPublic);
}
catch (Exception ex)
{
GC.KeepAlive(ex);
return null;
}
}
}
/// <summary>
/// Provides an awaiter for awaiting a <see cref="Task{TResult}" /> .
/// </summary>
/// <remarks>
/// This type is intended for compiler use only.
/// </remarks>
public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion
{
/// <summary>
/// The task being awaited.
/// </summary>
private readonly Task<TResult> _task;
/// <summary>
/// Gets whether the task being awaited is completed.
/// </summary>
/// <remarks>
/// This property is intended for compiler user rather than use directly in code.
/// </remarks>
/// <exception cref="NullReferenceException">The awaiter was not properly initialized.</exception>
public bool IsCompleted
{
get { return _task.IsCompleted; }
}
/// <summary>
/// Initializes the <see cref="TaskAwaiter{TResult}" /> .
/// </summary>
/// <param name="task"> The <see cref="Task{TResult}" /> to be awaited. </param>
internal TaskAwaiter(Task<TResult> task)
{
_task = task;
}
/// <summary>
/// Schedules the continuation onto the <see cref="Task" /> associated with this <see cref="TaskAwaiter" /> .
/// </summary>
/// <param name="continuation"> The action to invoke when the await operation completes. </param>
/// <exception cref="ArgumentNullException">The
/// <paramref name="continuation" />
/// argument is null (Nothing in Visual Basic).</exception>
/// <exception cref="NullReferenceException">The awaiter was not properly initialized.</exception>
/// <remarks>
/// This method is intended for compiler user rather than use directly in code.
/// </remarks>
public void OnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(_task, continuation, true);
}
/// <summary>
/// Schedules the continuation onto the <see cref="Task" /> associated with this <see cref="TaskAwaiter" /> .
/// </summary>
/// <param name="continuation"> The action to invoke when the await operation completes. </param>
/// <exception cref="ArgumentNullException">The
/// <paramref name="continuation" />
/// argument is null (Nothing in Visual Basic).</exception>
/// <exception cref="InvalidOperationException">The awaiter was not properly initialized.</exception>
/// <remarks>
/// This method is intended for compiler user rather than use directly in code.
/// </remarks>
[SecurityCritical]
public void UnsafeOnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(_task, continuation, true);
}
/// <summary>
/// Ends the await on the completed <see cref="Task{TResult}" /> .
/// </summary>
/// <returns> The result of the completed <see cref="Task{TResult}" /> . </returns>
/// <exception cref="NullReferenceException">The awaiter was not properly initialized.</exception>
/// <exception cref="InvalidOperationException">The task was not yet completed.</exception>
/// <exception cref="TaskCanceledException">The task was canceled.</exception>
/// <exception cref="Exception">The task completed in a Faulted state.</exception>
public TResult GetResult()
{
TaskAwaiter.ValidateEnd(_task);
return _task.Result;
}
}
}
#endif