Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / runtime / compilerservices / TaskAwaiter.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
7 //
8 // TaskAwaiter.cs
9 //
10 // <OWNER>Microsoft</OWNER>
11 //
12 // Types for awaiting Task and Task<T>. These types are emitted from Task{<T>}.GetAwaiter 
13 // and Task{<T>}.ConfigureAwait.  They are meant to be used only by the compiler, e.g.
14 // 
15 //   await nonGenericTask;
16 //   =====================
17 //       var $awaiter = nonGenericTask.GetAwaiter();
18 //       if (!$awaiter.IsCompleted)
19 //       {
20 //           SPILL:
21 //           $builder.AwaitUnsafeOnCompleted(ref $awaiter, ref this);
22 //           return;
23 //           Label:
24 //           UNSPILL;
25 //       }
26 //       $awaiter.GetResult();
27 //
28 //   result += await genericTask.ConfigureAwait(false);
29 //   ===================================================================================
30 //       var $awaiter = genericTask.ConfigureAwait(false).GetAwaiter();
31 //       if (!$awaiter.IsCompleted)
32 //       {
33 //           SPILL;
34 //           $builder.AwaitUnsafeOnCompleted(ref $awaiter, ref this);
35 //           return;
36 //           Label:
37 //           UNSPILL;
38 //       }
39 //       result += $awaiter.GetResult();
40 //
41 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
42
43 using System;
44 using System.Diagnostics;
45 using System.Diagnostics.CodeAnalysis;
46 using System.Diagnostics.Contracts;
47 using System.Security;
48 using System.Threading;
49 using System.Threading.Tasks;
50 using System.Security.Permissions;
51 using System.Diagnostics.Tracing;
52
53 // NOTE: For performance reasons, initialization is not verified.  If a developer
54 //       incorrectly initializes a task awaiter, which should only be done by the compiler,
55 //       NullReferenceExceptions may be generated (the alternative would be for us to detect
56 //       this case and then throw a different exception instead).  This is the same tradeoff
57 //       that's made with other compiler-focused value types like List<T>.Enumerator.
58
59 namespace System.Runtime.CompilerServices
60 {
61     /// <summary>Provides an awaiter for awaiting a <see cref="System.Threading.Tasks.Task"/>.</summary>
62     /// <remarks>This type is intended for compiler use only.</remarks>
63     [HostProtection(Synchronization = true, ExternalThreading = true)]
64     public struct TaskAwaiter : ICriticalNotifyCompletion
65     {
66         /// <summary>The task being awaited.</summary>
67         private readonly Task m_task;
68
69         /// <summary>Initializes the <see cref="TaskAwaiter"/>.</summary>
70         /// <param name="task">The <see cref="System.Threading.Tasks.Task"/> to be awaited.</param>
71         internal TaskAwaiter(Task task)
72         {
73             Contract.Requires(task != null, "Constructing an awaiter requires a task to await.");
74             m_task = task;
75         }
76
77         /// <summary>Gets whether the task being awaited is completed.</summary>
78         /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
79         /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
80         public bool IsCompleted 
81         {
82             get { return m_task.IsCompleted; }
83         }
84
85         /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary>
86         /// <param name="continuation">The action to invoke when the await operation completes.</param>
87         /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
88         /// <exception cref="System.InvalidOperationException">The awaiter was not properly initialized.</exception>
89         /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
90         [SecuritySafeCritical]
91         public void OnCompleted(Action continuation)
92         {
93             OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:true);
94         }
95
96         /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary>
97         /// <param name="continuation">The action to invoke when the await operation completes.</param>
98         /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
99         /// <exception cref="System.InvalidOperationException">The awaiter was not properly initialized.</exception>
100         /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
101         [SecurityCritical]
102         public void UnsafeOnCompleted(Action continuation)
103         {
104             OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:false);
105         }
106
107         /// <summary>Ends the await on the completed <see cref="System.Threading.Tasks.Task"/>.</summary>
108         /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
109         /// <exception cref="System.Threading.Tasks.TaskCanceledException">The task was canceled.</exception>
110         /// <exception cref="System.Exception">The task completed in a Faulted state.</exception>
111         public void GetResult()
112         {
113             ValidateEnd(m_task);
114         }
115
116         /// <summary>
117         /// Fast checks for the end of an await operation to determine whether more needs to be done
118         /// prior to completing the await.
119         /// </summary>
120         /// <param name="task">The awaited task.</param>
121         internal static void ValidateEnd(Task task)
122         {
123             // Fast checks that can be inlined.
124             if (task.IsWaitNotificationEnabledOrNotRanToCompletion)
125             {
126                 // If either the end await bit is set or we're not completed successfully,
127                 // fall back to the slower path.
128                 HandleNonSuccessAndDebuggerNotification(task);
129             }
130         }
131
132         /// <summary>
133         /// Ensures the task is completed, triggers any necessary debugger breakpoints for completing 
134         /// the await on the task, and throws an exception if the task did not complete successfully.
135         /// </summary>
136         /// <param name="task">The awaited task.</param>
137         private static void HandleNonSuccessAndDebuggerNotification(Task task)
138         {
139             // NOTE: The JIT refuses to inline ValidateEnd when it contains the contents
140             // of HandleNonSuccessAndDebuggerNotification, hence the separation.
141
142             // Synchronously wait for the task to complete.  When used by the compiler,
143             // the task will already be complete.  This code exists only for direct GetResult use,
144             // for cases where the same exception propagation semantics used by "await" are desired,
145             // but where for one reason or another synchronous rather than asynchronous waiting is needed.
146             if (!task.IsCompleted)
147             {
148                 bool taskCompleted = task.InternalWait(Timeout.Infinite, default(CancellationToken));
149                 Contract.Assert(taskCompleted, "With an infinite timeout, the task should have always completed.");
150             }
151
152             // Now that we're done, alert the debugger if so requested
153             task.NotifyDebuggerOfWaitCompletionIfNecessary();
154
155             // And throw an exception if the task is faulted or canceled.
156             if (!task.IsRanToCompletion) ThrowForNonSuccess(task);
157         }
158
159         /// <summary>Throws an exception to handle a task that completed in a state other than RanToCompletion.</summary>
160         private static void ThrowForNonSuccess(Task task)
161         {
162             Contract.Requires(task.IsCompleted, "Task must have been completed by now.");
163             Contract.Requires(task.Status != TaskStatus.RanToCompletion, "Task should not be completed successfully.");
164
165             // Handle whether the task has been canceled or faulted
166             switch (task.Status)
167             {
168                 // If the task completed in a canceled state, throw an OperationCanceledException.
169                 // This will either be the OCE that actually caused the task to cancel, or it will be a new
170                 // TaskCanceledException. TCE derives from OCE, and by throwing it we automatically pick up the
171                 // completed task's CancellationToken if it has one, including that CT in the OCE.
172                 case TaskStatus.Canceled:
173                     var oceEdi = task.GetCancellationExceptionDispatchInfo();
174                     if (oceEdi != null)
175                     {
176                         oceEdi.Throw();
177                         Contract.Assert(false, "Throw() should have thrown");
178                     }
179                     throw new TaskCanceledException(task);
180
181                 // If the task faulted, throw its first exception,
182                 // even if it contained more than one.
183                 case TaskStatus.Faulted:
184                     var edis = task.GetExceptionDispatchInfos();
185                     if (edis.Count > 0)
186                     {
187                         edis[0].Throw();
188                         Contract.Assert(false, "Throw() should have thrown");
189                         break; // Necessary to compile: non-reachable, but compiler can't determine that
190                     }
191                     else
192                     {
193                         Contract.Assert(false, "There should be exceptions if we're Faulted.");
194                         throw task.Exception;
195                     }
196             }
197         }
198
199         /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary>
200         /// <param name="task">The task being awaited.</param>
201         /// <param name="continuation">The action to invoke when the await operation completes.</param>
202         /// <param name="continueOnCapturedContext">Whether to capture and marshal back to the current context.</param>
203         /// <param name="flowExecutionContext">Whether to flow ExecutionContext across the await.</param>
204         /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
205         /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
206         /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
207         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable         
208         [SecurityCritical]
209         internal static void OnCompletedInternal(Task task, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext)
210         {
211             if (continuation == null) throw new ArgumentNullException("continuation");
212             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
213 #if !MONO
214             // If TaskWait* ETW events are enabled, trace a beginning event for this await
215             // and set up an ending event to be traced when the asynchronous await completes.
216             if ( TplEtwProvider.Log.IsEnabled() || Task.s_asyncDebuggingEnabled)
217             {
218                 continuation = OutputWaitEtwEvents(task, continuation);
219             }
220 #endif
221
222             // Set the continuation onto the awaited task.
223             task.SetContinuationForAwait(continuation, continueOnCapturedContext, flowExecutionContext, ref stackMark);
224         }
225
226         /// <summary>
227         /// Outputs a WaitBegin ETW event, and augments the continuation action to output a WaitEnd ETW event.
228         /// </summary>
229         /// <param name="task">The task being awaited.</param>
230         /// <param name="continuation">The action to invoke when the await operation completes.</param>
231         /// <returns>The action to use as the actual continuation.</returns>
232         private static Action OutputWaitEtwEvents(Task task, Action continuation)
233         {
234             Contract.Requires(task != null, "Need a task to wait on");
235             Contract.Requires(continuation != null, "Need a continuation to invoke when the wait completes");
236
237             if (Task.s_asyncDebuggingEnabled)
238             {
239                 Task.AddToActiveTasks(task);
240             }
241
242 #if !MONO
243             var etwLog = TplEtwProvider.Log;
244
245             if (etwLog.IsEnabled())
246             {
247                 // ETW event for Task Wait Begin
248                 var currentTaskAtBegin = Task.InternalCurrent;
249
250                 // If this task's continuation is another task, get it.
251                 var continuationTask = AsyncMethodBuilderCore.TryGetContinuationTask(continuation);
252                 etwLog.TaskWaitBegin(
253                     (currentTaskAtBegin != null ? currentTaskAtBegin.m_taskScheduler.Id : TaskScheduler.Default.Id),
254                     (currentTaskAtBegin != null ? currentTaskAtBegin.Id : 0),
255                     task.Id, TplEtwProvider.TaskWaitBehavior.Asynchronous, 
256                     (continuationTask != null ? continuationTask.Id : 0), System.Threading.Thread.GetDomainID());
257             }
258 #endif
259
260             // Create a continuation action that outputs the end event and then invokes the user
261             // provided delegate.  This incurs the allocations for the closure/delegate, but only if the event
262             // is enabled, and in doing so it allows us to pass the awaited task's information into the end event
263             // in a purely pay-for-play manner (the alternatively would be to increase the size of TaskAwaiter
264             // just for this ETW purpose, not pay-for-play, since GetResult would need to know whether a real yield occurred).
265             return AsyncMethodBuilderCore.CreateContinuationWrapper(continuation, () =>
266             {
267                 if (Task.s_asyncDebuggingEnabled)
268                 {
269                     Task.RemoveFromActiveTasks(task.Id);
270                 }
271 #if !MONO
272                 // ETW event for Task Wait End.
273                 Guid prevActivityId = new Guid();
274                 bool bEtwLogEnabled = etwLog.IsEnabled();
275                 if (bEtwLogEnabled)
276                 {
277                     var currentTaskAtEnd = Task.InternalCurrent;
278                     etwLog.TaskWaitEnd(
279                         (currentTaskAtEnd != null ? currentTaskAtEnd.m_taskScheduler.Id : TaskScheduler.Default.Id),
280                         (currentTaskAtEnd != null ? currentTaskAtEnd.Id : 0),
281                         task.Id);
282
283                     // Ensure the continuation runs under the activity ID of the task that completed for the
284                     // case the antecendent is a promise (in the other cases this is already the case).
285                     if (etwLog.TasksSetActivityIds && (task.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
286                         EventSource.SetCurrentThreadActivityId(TplEtwProvider.CreateGuidForTaskID(task.Id), out prevActivityId);
287                 }
288 #endif
289                 // Invoke the original continuation provided to OnCompleted.
290                 continuation();
291
292 #if !MONO
293                 if (bEtwLogEnabled)
294                 {
295                     etwLog.TaskWaitContinuationComplete(task.Id);
296                     if (etwLog.TasksSetActivityIds && (task.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
297                         EventSource.SetCurrentThreadActivityId(prevActivityId);
298                 }
299 #endif
300             });
301         }
302     }
303
304     /// <summary>Provides an awaiter for awaiting a <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary>
305     /// <remarks>This type is intended for compiler use only.</remarks>
306     [HostProtection(Synchronization = true, ExternalThreading = true)]
307     public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion
308     {
309         /// <summary>The task being awaited.</summary>
310         private readonly Task<TResult> m_task;
311
312         /// <summary>Initializes the <see cref="TaskAwaiter{TResult}"/>.</summary>
313         /// <param name="task">The <see cref="System.Threading.Tasks.Task{TResult}"/> to be awaited.</param>
314         internal TaskAwaiter(Task<TResult> task)
315         {
316             Contract.Requires(task != null, "Constructing an awaiter requires a task to await.");
317             m_task = task;
318         }
319
320         /// <summary>Gets whether the task being awaited is completed.</summary>
321         /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
322         /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
323         public bool IsCompleted 
324         {
325             get { return m_task.IsCompleted; }
326         }
327
328         /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary>
329         /// <param name="continuation">The action to invoke when the await operation completes.</param>
330         /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
331         /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
332         /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
333         [SecuritySafeCritical]
334         public void OnCompleted(Action continuation)
335         {
336             TaskAwaiter.OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:true);
337         }
338
339         /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary>
340         /// <param name="continuation">The action to invoke when the await operation completes.</param>
341         /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
342         /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
343         /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
344         [SecurityCritical]
345         public void UnsafeOnCompleted(Action continuation)
346         {
347             TaskAwaiter.OnCompletedInternal(m_task, continuation, continueOnCapturedContext:true, flowExecutionContext:false);
348         }
349
350         /// <summary>Ends the await on the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary>
351         /// <returns>The result of the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</returns>
352         /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
353         /// <exception cref="System.Threading.Tasks.TaskCanceledException">The task was canceled.</exception>
354         /// <exception cref="System.Exception">The task completed in a Faulted state.</exception>
355         public TResult GetResult()
356         {
357             TaskAwaiter.ValidateEnd(m_task);
358             return m_task.ResultOnSuccess;
359         }
360     }
361
362     /// <summary>Provides an awaitable object that allows for configured awaits on <see cref="System.Threading.Tasks.Task"/>.</summary>
363     /// <remarks>This type is intended for compiler use only.</remarks>
364     public struct ConfiguredTaskAwaitable
365     {
366         /// <summary>The task being awaited.</summary>
367         private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter m_configuredTaskAwaiter;
368
369         /// <summary>Initializes the <see cref="ConfiguredTaskAwaitable"/>.</summary>
370         /// <param name="task">The awaitable <see cref="System.Threading.Tasks.Task"/>.</param>
371         /// <param name="continueOnCapturedContext">
372         /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
373         /// </param>
374         internal ConfiguredTaskAwaitable(Task task, bool continueOnCapturedContext)
375         {
376             Contract.Requires(task != null, "Constructing an awaitable requires a task to await.");
377             m_configuredTaskAwaiter = new ConfiguredTaskAwaitable.ConfiguredTaskAwaiter(task, continueOnCapturedContext);
378         }
379
380         /// <summary>Gets an awaiter for this awaitable.</summary>
381         /// <returns>The awaiter.</returns>
382         public ConfiguredTaskAwaitable.ConfiguredTaskAwaiter GetAwaiter()
383         {
384             return m_configuredTaskAwaiter;
385         }
386
387         /// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable"/>.</summary>
388         /// <remarks>This type is intended for compiler use only.</remarks>
389         [HostProtection(Synchronization = true, ExternalThreading = true)]
390         public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion
391         {
392             /// <summary>The task being awaited.</summary>
393             private readonly Task m_task;
394             /// <summary>Whether to attempt marshaling back to the original context.</summary>
395             private readonly bool m_continueOnCapturedContext;
396
397             /// <summary>Initializes the <see cref="ConfiguredTaskAwaiter"/>.</summary>
398             /// <param name="task">The <see cref="System.Threading.Tasks.Task"/> to await.</param>
399             /// <param name="continueOnCapturedContext">
400             /// true to attempt to marshal the continuation back to the original context captured
401             /// when BeginAwait is called; otherwise, false.
402             /// </param>
403             internal ConfiguredTaskAwaiter(Task task, bool continueOnCapturedContext)
404             {
405                 Contract.Requires(task != null, "Constructing an awaiter requires a task to await.");
406                 m_task = task;
407                 m_continueOnCapturedContext = continueOnCapturedContext;
408             }
409
410             /// <summary>Gets whether the task being awaited is completed.</summary>
411             /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
412             /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
413             public bool IsCompleted 
414             {
415                 get { return m_task.IsCompleted; }
416             }
417
418             /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary>
419             /// <param name="continuation">The action to invoke when the await operation completes.</param>
420             /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
421             /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
422             /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
423             [SecuritySafeCritical]
424             public void OnCompleted(Action continuation)
425             {
426                 TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:true);
427             }
428
429             /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary>
430             /// <param name="continuation">The action to invoke when the await operation completes.</param>
431             /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
432             /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
433             /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
434             [SecurityCritical]
435             public void UnsafeOnCompleted(Action continuation)
436             {
437                 TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:false);
438             }
439
440             /// <summary>Ends the await on the completed <see cref="System.Threading.Tasks.Task"/>.</summary>
441             /// <returns>The result of the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</returns>
442             /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
443             /// <exception cref="System.Threading.Tasks.TaskCanceledException">The task was canceled.</exception>
444             /// <exception cref="System.Exception">The task completed in a Faulted state.</exception>
445             public void GetResult()
446             {
447                 TaskAwaiter.ValidateEnd(m_task);
448             }
449         }
450     }
451
452     /// <summary>Provides an awaitable object that allows for configured awaits on <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary>
453     /// <remarks>This type is intended for compiler use only.</remarks>
454     public struct ConfiguredTaskAwaitable<TResult>
455     {
456         /// <summary>The underlying awaitable on whose logic this awaitable relies.</summary>
457         private readonly ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter m_configuredTaskAwaiter;
458
459         /// <summary>Initializes the <see cref="ConfiguredTaskAwaitable{TResult}"/>.</summary>
460         /// <param name="task">The awaitable <see cref="System.Threading.Tasks.Task{TResult}"/>.</param>
461         /// <param name="continueOnCapturedContext">
462         /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
463         /// </param>
464         internal ConfiguredTaskAwaitable(Task<TResult> task, bool continueOnCapturedContext)
465         {
466             m_configuredTaskAwaiter = new ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter(task, continueOnCapturedContext);
467         }
468
469         /// <summary>Gets an awaiter for this awaitable.</summary>
470         /// <returns>The awaiter.</returns>
471         public ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter GetAwaiter()
472         {
473             return m_configuredTaskAwaiter;
474         }
475
476         /// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable{TResult}"/>.</summary>
477         /// <remarks>This type is intended for compiler use only.</remarks>
478         [HostProtection(Synchronization = true, ExternalThreading = true)]
479         public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion
480         {
481             /// <summary>The task being awaited.</summary>
482             private readonly Task<TResult> m_task;
483             /// <summary>Whether to attempt marshaling back to the original context.</summary>
484             private readonly bool m_continueOnCapturedContext;
485
486             /// <summary>Initializes the <see cref="ConfiguredTaskAwaiter"/>.</summary>
487             /// <param name="task">The awaitable <see cref="System.Threading.Tasks.Task{TResult}"/>.</param>
488             /// <param name="continueOnCapturedContext">
489             /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
490             /// </param>
491             internal ConfiguredTaskAwaiter(Task<TResult> task, bool continueOnCapturedContext)
492             {
493                 Contract.Requires(task != null, "Constructing an awaiter requires a task to await.");
494                 m_task = task;
495                 m_continueOnCapturedContext = continueOnCapturedContext;
496             }
497
498             /// <summary>Gets whether the task being awaited is completed.</summary>
499             /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
500             /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
501             public bool IsCompleted 
502             {
503                 get { return m_task.IsCompleted; }
504             }
505
506             /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary>
507             /// <param name="continuation">The action to invoke when the await operation completes.</param>
508             /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
509             /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
510             /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
511             [SecuritySafeCritical]
512             public void OnCompleted(Action continuation)
513             {
514                 TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:true);
515             }
516
517             /// <summary>Schedules the continuation onto the <see cref="System.Threading.Tasks.Task"/> associated with this <see cref="TaskAwaiter"/>.</summary>
518             /// <param name="continuation">The action to invoke when the await operation completes.</param>
519             /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
520             /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
521             /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
522             [SecurityCritical]
523             public void UnsafeOnCompleted(Action continuation)
524             {
525                 TaskAwaiter.OnCompletedInternal(m_task, continuation, m_continueOnCapturedContext, flowExecutionContext:false);
526             }
527
528             /// <summary>Ends the await on the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary>
529             /// <returns>The result of the completed <see cref="System.Threading.Tasks.Task{TResult}"/>.</returns>
530             /// <exception cref="System.NullReferenceException">The awaiter was not properly initialized.</exception>
531             /// <exception cref="System.Threading.Tasks.TaskCanceledException">The task was canceled.</exception>
532             /// <exception cref="System.Exception">The task completed in a Faulted state.</exception>
533             public TResult GetResult()
534             {
535                 TaskAwaiter.ValidateEnd(m_task);
536                 return m_task.ResultOnSuccess;
537             }
538         }
539     }
540 }