#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