#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
{
///
/// Provides an awaiter for awaiting a .
///
///
/// This type is intended for compiler use only.
///
public struct TaskAwaiter : ICriticalNotifyCompletion
{
///
/// A MethodInfo for the Exception.PrepForRemoting method.
///
private static readonly MethodInfo _prepForRemoting = GetPrepForRemotingMethodInfo();
///
/// An empty array to use with MethodInfo.Invoke.
///
private static readonly object[] _emptyParams = new object[0];
///
/// The default value to use for continueOnCapturedContext.
///
internal const bool continueOnCapturedContextDefaultValue = true;
///
/// Error message for GetAwaiter.
///
private const string _invalidOperationExceptionTaskNotCompleted = "The task has not yet completed."; // TODO not used
///
/// The task being awaited.
///
private readonly Task _task;
///
/// Gets whether the task being awaited is completed.
///
///
/// This property is intended for compiler user rather than use directly in code.
///
/// The awaiter was not properly initialized.
public bool IsCompleted
{
get { return _task.IsCompleted; }
}
///
/// Whether the current thread is appropriate for inlining the await continuation.
///
private static bool IsValidLocationForInlining
{
get
{
var current = SynchronizationContext.Current;
if (current != null && current.GetType() != typeof(SynchronizationContext))
{
return false;
}
return TaskScheduler.Current == TaskScheduler.Default;
}
}
///
/// Initializes the .
///
/// The to be awaited.
internal TaskAwaiter(Task task)
{
_task = task;
}
///
/// Schedules the continuation onto the associated with this .
///
/// The action to invoke when the await operation completes.
/// The
///
/// argument is null (Nothing in Visual Basic).
/// The awaiter was not properly initialized.
///
/// This method is intended for compiler user rather than use directly in code.
///
public void OnCompleted(Action continuation)
{
OnCompletedInternal(_task, continuation, true);
}
///
/// Schedules the continuation onto the associated with this .
///
/// The action to invoke when the await operation completes.
/// The
///
/// argument is null (Nothing in Visual Basic).
/// The awaiter was not properly initialized.
///
/// This method is intended for compiler user rather than use directly in code.
///
[SecurityCritical]
public void UnsafeOnCompleted(Action continuation)
{
OnCompletedInternal(_task, continuation, true);
}
///
/// Ends the await on the completed .
///
/// The awaiter was not properly initialized.
/// The task was not yet completed.
/// The task was canceled.
/// The task completed in a Faulted state.
public void GetResult()
{
ValidateEnd(_task);
}
///
/// Fast checks for the end of an await operation to determine whether more needs to be done prior to completing the await.
///
/// The awaited task.
internal static void ValidateEnd(Task task)
{
if (task.Status == TaskStatus.RanToCompletion)
{
return;
}
HandleNonSuccess(task);
}
///
/// Handles validations on tasks that aren't successfully completed.
///
/// The awaited task.
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.");
}
}
///
/// Schedules the continuation onto the associated with this .
///
/// The awaited task.
/// The action to invoke when the await operation completes.
/// Whether to capture and marshal back to the current context.
/// The
///
/// argument is null (Nothing in Visual Basic).
/// The awaiter was not properly initialized.
///
/// This method is intended for compiler user rather than use directly in code.
///
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);
}
}
///
/// Copies the exception's stack trace so its stack trace isn't overwritten.
///
/// The exception to prepare.
internal static Exception PrepareExceptionForRethrow(Exception exc)
{
if (_prepForRemoting != null)
{
try
{
_prepForRemoting.Invoke(exc, _emptyParams);
}
catch (Exception ex)
{
GC.KeepAlive(ex);
}
}
return exc;
}
///
/// Gets the MethodInfo for the internal PrepForRemoting method on Exception.
///
/// The MethodInfo if it could be retrieved, or else null.
private static MethodInfo GetPrepForRemotingMethodInfo()
{
try
{
return typeof(Exception).GetMethod("PrepForRemoting", BindingFlags.Instance | BindingFlags.NonPublic);
}
catch (Exception ex)
{
GC.KeepAlive(ex);
return null;
}
}
}
///
/// Provides an awaiter for awaiting a .
///
///
/// This type is intended for compiler use only.
///
public struct TaskAwaiter : ICriticalNotifyCompletion
{
///
/// The task being awaited.
///
private readonly Task _task;
///
/// Gets whether the task being awaited is completed.
///
///
/// This property is intended for compiler user rather than use directly in code.
///
/// The awaiter was not properly initialized.
public bool IsCompleted
{
get { return _task.IsCompleted; }
}
///
/// Initializes the .
///
/// The to be awaited.
internal TaskAwaiter(Task task)
{
_task = task;
}
///
/// Schedules the continuation onto the associated with this .
///
/// The action to invoke when the await operation completes.
/// The
///
/// argument is null (Nothing in Visual Basic).
/// The awaiter was not properly initialized.
///
/// This method is intended for compiler user rather than use directly in code.
///
public void OnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(_task, continuation, true);
}
///
/// Schedules the continuation onto the associated with this .
///
/// The action to invoke when the await operation completes.
/// The
///
/// argument is null (Nothing in Visual Basic).
/// The awaiter was not properly initialized.
///
/// This method is intended for compiler user rather than use directly in code.
///
[SecurityCritical]
public void UnsafeOnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(_task, continuation, true);
}
///
/// Ends the await on the completed .
///
/// The result of the completed .
/// The awaiter was not properly initialized.
/// The task was not yet completed.
/// The task was canceled.
/// The task completed in a Faulted state.
public TResult GetResult()
{
TaskAwaiter.ValidateEnd(_task);
return _task.Result;
}
}
}
#endif