022f15ab1108b3ece6036fda1590b779103502bb
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Runtime / CompilerServices / AsyncMethodBuilder.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6 //
7 //
8 //
9 // Compiler-targeted types that build tasks for use as the return types of asynchronous methods.
10 //
11 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
12
13 using System.Diagnostics;
14 using System.Diagnostics.Tracing;
15 using System.Reflection;
16 using System.Runtime.ExceptionServices;
17 #if FEATURE_COMINTEROP
18 using System.Runtime.InteropServices.WindowsRuntime;
19 #endif // FEATURE_COMINTEROP
20 using System.Threading;
21 using System.Threading.Tasks;
22 using System.Text;
23 using Internal.Runtime.CompilerServices;
24
25 namespace System.Runtime.CompilerServices
26 {
27     /// <summary>
28     /// Provides a builder for asynchronous methods that return void.
29     /// This type is intended for compiler use only.
30     /// </summary>
31     public struct AsyncVoidMethodBuilder
32     {
33         /// <summary>The synchronization context associated with this operation.</summary>
34         private SynchronizationContext _synchronizationContext;
35         /// <summary>The builder this void builder wraps.</summary>
36         private AsyncTaskMethodBuilder _builder; // mutable struct: must not be readonly
37
38         /// <summary>Initializes a new <see cref="AsyncVoidMethodBuilder"/>.</summary>
39         /// <returns>The initialized <see cref="AsyncVoidMethodBuilder"/>.</returns>
40         public static AsyncVoidMethodBuilder Create()
41         {
42             SynchronizationContext sc = SynchronizationContext.Current;
43             sc?.OperationStarted();
44             return new AsyncVoidMethodBuilder() { _synchronizationContext = sc };
45         }
46
47         /// <summary>Initiates the builder's execution with the associated state machine.</summary>
48         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
49         /// <param name="stateMachine">The state machine instance, passed by reference.</param>
50         /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception>
51         [DebuggerStepThrough]
52         public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
53             _builder.Start(ref stateMachine);
54
55         /// <summary>Associates the builder with the state machine it represents.</summary>
56         /// <param name="stateMachine">The heap-allocated state machine object.</param>
57         /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception>
58         /// <exception cref="System.InvalidOperationException">The builder is incorrectly initialized.</exception>
59         public void SetStateMachine(IAsyncStateMachine stateMachine) =>
60             _builder.SetStateMachine(stateMachine);
61
62         /// <summary>
63         /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
64         /// </summary>
65         /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
66         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
67         /// <param name="awaiter">The awaiter.</param>
68         /// <param name="stateMachine">The state machine.</param>
69         public void AwaitOnCompleted<TAwaiter, TStateMachine>(
70             ref TAwaiter awaiter, ref TStateMachine stateMachine)
71             where TAwaiter : INotifyCompletion
72             where TStateMachine : IAsyncStateMachine =>
73             _builder.AwaitOnCompleted(ref awaiter, ref stateMachine);
74
75         /// <summary>
76         /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
77         /// </summary>
78         /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
79         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
80         /// <param name="awaiter">The awaiter.</param>
81         /// <param name="stateMachine">The state machine.</param>
82         public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
83             ref TAwaiter awaiter, ref TStateMachine stateMachine)
84             where TAwaiter : ICriticalNotifyCompletion
85             where TStateMachine : IAsyncStateMachine =>
86             _builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
87
88         /// <summary>Completes the method builder successfully.</summary>
89         public void SetResult()
90         {
91             if (AsyncCausalityTracer.LoggingOn)
92             {
93                 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Task.Id, AsyncCausalityStatus.Completed);
94             }
95
96             // Mark the builder as completed.  As this is a void-returning method, this mostly
97             // doesn't matter, but it can affect things like debug events related to finalization.
98             _builder.SetResult();
99
100             if (_synchronizationContext != null)
101             {
102                 NotifySynchronizationContextOfCompletion();
103             }
104         }
105
106         /// <summary>Faults the method builder with an exception.</summary>
107         /// <param name="exception">The exception that is the cause of this fault.</param>
108         /// <exception cref="System.ArgumentNullException">The <paramref name="exception"/> argument is null (Nothing in Visual Basic).</exception>
109         /// <exception cref="System.InvalidOperationException">The builder is not initialized.</exception>
110         public void SetException(Exception exception)
111         {
112             if (exception == null)
113             {
114                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception);
115             }
116
117             if (AsyncCausalityTracer.LoggingOn)
118             {
119                 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Task.Id, AsyncCausalityStatus.Error);
120             }
121
122             if (_synchronizationContext != null)
123             {
124                 // If we captured a synchronization context, Post the throwing of the exception to it 
125                 // and decrement its outstanding operation count.
126                 try
127                 {
128                     AsyncMethodBuilderCore.ThrowAsync(exception, targetContext: _synchronizationContext);
129                 }
130                 finally
131                 {
132                     NotifySynchronizationContextOfCompletion();
133                 }
134             }
135             else
136             {
137                 // Otherwise, queue the exception to be thrown on the ThreadPool.  This will
138                 // result in a crash unless legacy exception behavior is enabled by a config
139                 // file or a CLR host.
140                 AsyncMethodBuilderCore.ThrowAsync(exception, targetContext: null);
141             }
142
143             // The exception was propagated already; we don't need or want to fault the builder, just mark it as completed.
144             _builder.SetResult();
145         }
146
147         /// <summary>Notifies the current synchronization context that the operation completed.</summary>
148         private void NotifySynchronizationContextOfCompletion()
149         {
150             Debug.Assert(_synchronizationContext != null, "Must only be used with a non-null context.");
151             try
152             {
153                 _synchronizationContext.OperationCompleted();
154             }
155             catch (Exception exc)
156             {
157                 // If the interaction with the SynchronizationContext goes awry,
158                 // fall back to propagating on the ThreadPool.
159                 AsyncMethodBuilderCore.ThrowAsync(exc, targetContext: null);
160             }
161         }
162
163         /// <summary>Lazily instantiate the Task in a non-thread-safe manner.</summary>
164         private Task Task => _builder.Task;
165
166         /// <summary>
167         /// Gets an object that may be used to uniquely identify this builder to the debugger.
168         /// </summary>
169         /// <remarks>
170         /// This property lazily instantiates the ID in a non-thread-safe manner.  
171         /// It must only be used by the debugger and AsyncCausalityTracer in a single-threaded manner.
172         /// </remarks>
173         internal object ObjectIdForDebugger => _builder.ObjectIdForDebugger;
174     }
175
176     /// <summary>
177     /// Provides a builder for asynchronous methods that return <see cref="System.Threading.Tasks.Task"/>.
178     /// This type is intended for compiler use only.
179     /// </summary>
180     /// <remarks>
181     /// AsyncTaskMethodBuilder is a value type, and thus it is copied by value.
182     /// Prior to being copied, one of its Task, SetResult, or SetException members must be accessed,
183     /// or else the copies may end up building distinct Task instances.
184     /// </remarks>
185     public struct AsyncTaskMethodBuilder
186     {
187         /// <summary>A cached VoidTaskResult task used for builders that complete synchronously.</summary>
188         private readonly static Task<VoidTaskResult> s_cachedCompleted = AsyncTaskMethodBuilder<VoidTaskResult>.s_defaultResultTask;
189
190         /// <summary>The generic builder object to which this non-generic instance delegates.</summary>
191         private AsyncTaskMethodBuilder<VoidTaskResult> m_builder; // mutable struct: must not be readonly. Debugger depends on the exact name of this field.
192
193         /// <summary>Initializes a new <see cref="AsyncTaskMethodBuilder"/>.</summary>
194         /// <returns>The initialized <see cref="AsyncTaskMethodBuilder"/>.</returns>
195         public static AsyncTaskMethodBuilder Create() => default(AsyncTaskMethodBuilder);
196
197         /// <summary>Initiates the builder's execution with the associated state machine.</summary>
198         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
199         /// <param name="stateMachine">The state machine instance, passed by reference.</param>
200         [DebuggerStepThrough]
201         public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
202             m_builder.Start(ref stateMachine);
203
204         /// <summary>Associates the builder with the state machine it represents.</summary>
205         /// <param name="stateMachine">The heap-allocated state machine object.</param>
206         /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception>
207         /// <exception cref="System.InvalidOperationException">The builder is incorrectly initialized.</exception>
208         public void SetStateMachine(IAsyncStateMachine stateMachine) =>
209             m_builder.SetStateMachine(stateMachine);
210
211         /// <summary>
212         /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
213         /// </summary>
214         /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
215         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
216         /// <param name="awaiter">The awaiter.</param>
217         /// <param name="stateMachine">The state machine.</param>
218         public void AwaitOnCompleted<TAwaiter, TStateMachine>(
219             ref TAwaiter awaiter, ref TStateMachine stateMachine)
220             where TAwaiter : INotifyCompletion
221             where TStateMachine : IAsyncStateMachine =>
222             m_builder.AwaitOnCompleted(ref awaiter, ref stateMachine);
223
224         /// <summary>
225         /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
226         /// </summary>
227         /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
228         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
229         /// <param name="awaiter">The awaiter.</param>
230         /// <param name="stateMachine">The state machine.</param>
231         public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
232             ref TAwaiter awaiter, ref TStateMachine stateMachine)
233             where TAwaiter : ICriticalNotifyCompletion
234             where TStateMachine : IAsyncStateMachine =>
235             m_builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
236
237         /// <summary>Gets the <see cref="System.Threading.Tasks.Task"/> for this builder.</summary>
238         /// <returns>The <see cref="System.Threading.Tasks.Task"/> representing the builder's asynchronous operation.</returns>
239         /// <exception cref="System.InvalidOperationException">The builder is not initialized.</exception>
240         public Task Task
241         {
242             [MethodImpl(MethodImplOptions.AggressiveInlining)]
243             get => m_builder.Task;
244         }
245
246         /// <summary>
247         /// Completes the <see cref="System.Threading.Tasks.Task"/> in the 
248         /// <see cref="System.Threading.Tasks.TaskStatus">RanToCompletion</see> state.
249         /// </summary>
250         /// <exception cref="System.InvalidOperationException">The builder is not initialized.</exception>
251         /// <exception cref="System.InvalidOperationException">The task has already completed.</exception>
252         public void SetResult() => m_builder.SetResult(s_cachedCompleted); // Using s_cachedCompleted is faster than using s_defaultResultTask.
253
254         /// <summary>
255         /// Completes the <see cref="System.Threading.Tasks.Task"/> in the 
256         /// <see cref="System.Threading.Tasks.TaskStatus">Faulted</see> state with the specified exception.
257         /// </summary>
258         /// <param name="exception">The <see cref="System.Exception"/> to use to fault the task.</param>
259         /// <exception cref="System.ArgumentNullException">The <paramref name="exception"/> argument is null (Nothing in Visual Basic).</exception>
260         /// <exception cref="System.InvalidOperationException">The builder is not initialized.</exception>
261         /// <exception cref="System.InvalidOperationException">The task has already completed.</exception>
262         public void SetException(Exception exception) => m_builder.SetException(exception);
263
264         /// <summary>
265         /// Called by the debugger to request notification when the first wait operation
266         /// (await, Wait, Result, etc.) on this builder's task completes.
267         /// </summary>
268         /// <param name="enabled">
269         /// true to enable notification; false to disable a previously set notification.
270         /// </param>
271         internal void SetNotificationForWaitCompletion(bool enabled) => m_builder.SetNotificationForWaitCompletion(enabled);
272
273         /// <summary>
274         /// Gets an object that may be used to uniquely identify this builder to the debugger.
275         /// </summary>
276         /// <remarks>
277         /// This property lazily instantiates the ID in a non-thread-safe manner.  
278         /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner
279         /// when no other threads are in the middle of accessing this property or this.Task.
280         /// </remarks>
281         internal object ObjectIdForDebugger => m_builder.ObjectIdForDebugger;
282     }
283
284     /// <summary>
285     /// Provides a builder for asynchronous methods that return <see cref="System.Threading.Tasks.Task{TResult}"/>.
286     /// This type is intended for compiler use only.
287     /// </summary>
288     /// <remarks>
289     /// AsyncTaskMethodBuilder{TResult} is a value type, and thus it is copied by value.
290     /// Prior to being copied, one of its Task, SetResult, or SetException members must be accessed,
291     /// or else the copies may end up building distinct Task instances.
292     /// </remarks>
293     public struct AsyncTaskMethodBuilder<TResult>
294     {
295         /// <summary>A cached task for default(TResult).</summary>
296         internal readonly static Task<TResult> s_defaultResultTask = AsyncTaskCache.CreateCacheableTask(default(TResult));
297
298         /// <summary>The lazily-initialized built task.</summary>
299         private Task<TResult> m_task; // lazily-initialized: must not be readonly. Debugger depends on the exact name of this field.
300
301         /// <summary>Initializes a new <see cref="AsyncTaskMethodBuilder"/>.</summary>
302         /// <returns>The initialized <see cref="AsyncTaskMethodBuilder"/>.</returns>
303         public static AsyncTaskMethodBuilder<TResult> Create()
304         {
305             return default(AsyncTaskMethodBuilder<TResult>);
306             // NOTE: If this method is ever updated to perform more initialization,
307             //       other Create methods like AsyncTaskMethodBuilder.Create and
308             //       AsyncValueTaskMethodBuilder.Create must be updated to call this.
309         }
310
311         /// <summary>Initiates the builder's execution with the associated state machine.</summary>
312         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
313         /// <param name="stateMachine">The state machine instance, passed by reference.</param>
314         [DebuggerStepThrough]
315         public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
316         {
317             if (stateMachine == null) // TStateMachines are generally non-nullable value types, so this check will be elided
318             {
319                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine);
320             }
321
322             // Run the MoveNext method within a copy-on-write ExecutionContext scope.
323             // This allows us to undo any ExecutionContext changes made in MoveNext,
324             // so that they won't "leak" out of the first await.
325
326             Thread currentThread = Thread.CurrentThread;
327             ExecutionContextSwitcher ecs = default(ExecutionContextSwitcher);
328             try
329             {
330                 ExecutionContext.EstablishCopyOnWriteScope(currentThread, ref ecs);
331                 stateMachine.MoveNext();
332             }
333             finally
334             {
335                 ecs.Undo(currentThread);
336             }
337         }
338
339         /// <summary>Associates the builder with the state machine it represents.</summary>
340         /// <param name="stateMachine">The heap-allocated state machine object.</param>
341         /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception>
342         /// <exception cref="System.InvalidOperationException">The builder is incorrectly initialized.</exception>
343         public void SetStateMachine(IAsyncStateMachine stateMachine)
344         {
345             if (stateMachine == null)
346             {
347                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine);
348             }
349
350             if (m_task != null)
351             {
352                 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.AsyncMethodBuilder_InstanceNotInitialized);
353             }
354
355             // SetStateMachine was originally needed in order to store the boxed state machine reference into
356             // the boxed copy.  Now that a normal box is no longer used, SetStateMachine is also legacy.  We need not
357             // do anything here, and thus assert to ensure we're not calling this from our own implementations.
358             Debug.Fail("SetStateMachine should not be used.");
359         }
360
361         /// <summary>
362         /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
363         /// </summary>
364         /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
365         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
366         /// <param name="awaiter">The awaiter.</param>
367         /// <param name="stateMachine">The state machine.</param>
368         public void AwaitOnCompleted<TAwaiter, TStateMachine>(
369             ref TAwaiter awaiter, ref TStateMachine stateMachine)
370             where TAwaiter : INotifyCompletion
371             where TStateMachine : IAsyncStateMachine
372         {
373             try
374             {
375                 awaiter.OnCompleted(GetStateMachineBox(ref stateMachine).MoveNextAction);
376             }
377             catch (Exception e)
378             {
379                 AsyncMethodBuilderCore.ThrowAsync(e, targetContext: null);
380             }
381         }
382
383         /// <summary>
384         /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
385         /// </summary>
386         /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
387         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
388         /// <param name="awaiter">The awaiter.</param>
389         /// <param name="stateMachine">The state machine.</param>
390         public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
391             ref TAwaiter awaiter, ref TStateMachine stateMachine)
392             where TAwaiter : ICriticalNotifyCompletion
393             where TStateMachine : IAsyncStateMachine
394         {
395             IAsyncStateMachineBox box = GetStateMachineBox(ref stateMachine);
396
397             // The null tests here ensure that the jit can optimize away the interface
398             // tests when TAwaiter is is a ref type.
399             if ((null != (object)default(TAwaiter)) && (awaiter is ITaskAwaiter))
400             {
401                 ref TaskAwaiter ta = ref Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter); // relies on TaskAwaiter/TaskAwaiter<T> having the same layout
402                 TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, continueOnCapturedContext: true);
403             }
404             else if ((null != (object)default(TAwaiter)) && (awaiter is IConfiguredTaskAwaiter))
405             {
406                 ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter ta = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(ref awaiter);
407                 TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, ta.m_continueOnCapturedContext);
408             }
409             else if ((null != (object)default(TAwaiter)) && (awaiter is IValueTaskAwaiter))
410             {
411                 Task t = ((IValueTaskAwaiter)awaiter).GetTask();
412                 TaskAwaiter.UnsafeOnCompletedInternal(t, box, continueOnCapturedContext: true);
413             }
414             else if ((null != (object)default(TAwaiter)) && (awaiter is IConfiguredValueTaskAwaiter))
415             {
416                 Task t = ((IConfiguredValueTaskAwaiter)awaiter).GetTask(out bool continueOnCapturedContext);
417                 TaskAwaiter.UnsafeOnCompletedInternal(t, box, continueOnCapturedContext);
418             }
419             // The awaiter isn't specially known. Fall back to doing a normal await.
420             else
421             {
422                 try
423                 {
424                     awaiter.UnsafeOnCompleted(box.MoveNextAction);
425                 }
426                 catch (Exception e)
427                 {
428                     AsyncMethodBuilderCore.ThrowAsync(e, targetContext: null);
429                 }
430             }
431         }
432
433         /// <summary>Gets the "boxed" state machine object.</summary>
434         /// <typeparam name="TStateMachine">Specifies the type of the async state machine.</typeparam>
435         /// <param name="stateMachine">The state machine.</param>
436         /// <returns>The "boxed" state machine.</returns>
437         private IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(
438             ref TStateMachine stateMachine)
439             where TStateMachine : IAsyncStateMachine
440         {
441             ExecutionContext currentContext = ExecutionContext.Capture();
442
443             // Check first for the most common case: not the first yield in an async method.
444             // In this case, the first yield will have already "boxed" the state machine in
445             // a strongly-typed manner into an AsyncStateMachineBox.  It will already contain
446             // the state machine as well as a MoveNextDelegate and a context.  The only thing
447             // we might need to do is update the context if that's changed since it was stored.
448             if (m_task is AsyncStateMachineBox<TStateMachine> stronglyTypedBox)
449             {
450                 if (stronglyTypedBox.Context != currentContext)
451                 {
452                     stronglyTypedBox.Context = currentContext;
453                 }
454                 return stronglyTypedBox;
455             }
456
457             // The least common case: we have a weakly-typed boxed.  This results if the debugger
458             // or some other use of reflection accesses a property like ObjectIdForDebugger or a
459             // method like SetNotificationForWaitCompletion prior to the first await happening.  In
460             // such situations, we need to get an object to represent the builder, but we don't yet
461             // know the type of the state machine, and thus can't use TStateMachine.  Instead, we
462             // use the IAsyncStateMachine interface, which all TStateMachines implement.  This will
463             // result in a boxing allocation when storing the TStateMachine if it's a struct, but
464             // this only happens in active debugging scenarios where such performance impact doesn't
465             // matter.
466             if (m_task is AsyncStateMachineBox<IAsyncStateMachine> weaklyTypedBox)
467             {
468                 // If this is the first await, we won't yet have a state machine, so store it.
469                 if (weaklyTypedBox.StateMachine == null)
470                 {
471                     Debugger.NotifyOfCrossThreadDependency(); // same explanation as with usage below
472                     weaklyTypedBox.StateMachine = stateMachine;
473                 }
474
475                 // Update the context.  This only happens with a debugger, so no need to spend
476                 // extra IL checking for equality before doing the assignment.
477                 weaklyTypedBox.Context = currentContext;
478                 return weaklyTypedBox;
479             }
480
481             // Alert a listening debugger that we can't make forward progress unless it slips threads.
482             // If we don't do this, and a method that uses "await foo;" is invoked through funceval,
483             // we could end up hooking up a callback to push forward the async method's state machine,
484             // the debugger would then abort the funceval after it takes too long, and then continuing
485             // execution could result in another callback being hooked up.  At that point we have
486             // multiple callbacks registered to push the state machine, which could result in bad behavior.
487             Debugger.NotifyOfCrossThreadDependency();
488
489             // At this point, m_task should really be null, in which case we want to create the box.
490             // However, in a variety of debugger-related (erroneous) situations, it might be non-null,
491             // e.g. if the Task property is examined in a Watch window, forcing it to be lazily-intialized
492             // as a Task<TResult> rather than as an AsyncStateMachineBox.  The worst that happens in such
493             // cases is we lose the ability to properly step in the debugger, as the debugger uses that
494             // object's identity to track this specific builder/state machine.  As such, we proceed to
495             // overwrite whatever's there anyway, even if it's non-null.
496             var box = AsyncMethodBuilderCore.TrackAsyncMethodCompletion ?
497                 new DebugFinalizableAsyncStateMachineBox<TStateMachine>() :
498                 new AsyncStateMachineBox<TStateMachine>();
499             m_task = box; // important: this must be done before storing stateMachine into box.StateMachine!
500             box.StateMachine = stateMachine;
501             box.Context = currentContext;
502             return box;
503         }
504
505         /// <summary>
506         /// Provides an async state machine box with a finalizer that will fire an EventSource
507         /// event about the state machine if it's being finalized without having been completed.
508         /// </summary>
509         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
510         private sealed class DebugFinalizableAsyncStateMachineBox<TStateMachine> :
511             AsyncStateMachineBox<TStateMachine>
512             where TStateMachine : IAsyncStateMachine
513         {
514             ~DebugFinalizableAsyncStateMachineBox()
515             {
516                 // If the state machine is being finalized, something went wrong during its processing,
517                 // e.g. it awaited something that got collected without itself having been completed.
518                 // Fire an event with details about the state machine to help with debugging.
519                 if (!IsCompleted) // double-check it's not completed, just to help minimize false positives
520                 {
521                     TplEtwProvider.Log.IncompleteAsyncMethod(this);
522                 }
523             }
524         }
525
526         /// <summary>A strongly-typed box for Task-based async state machines.</summary>
527         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
528         /// <typeparam name="TResult">Specifies the type of the Task's result.</typeparam>
529         private class AsyncStateMachineBox<TStateMachine> :
530             Task<TResult>, IAsyncStateMachineBox
531             where TStateMachine : IAsyncStateMachine
532         {
533             /// <summary>Delegate used to invoke on an ExecutionContext when passed an instance of this box type.</summary>
534             private static readonly ContextCallback s_callback = s => ((AsyncStateMachineBox<TStateMachine>)s).StateMachine.MoveNext();
535
536             /// <summary>A delegate to the <see cref="MoveNext"/> method.</summary>
537             private Action _moveNextAction;
538             /// <summary>The state machine itself.</summary>
539             public TStateMachine StateMachine; // mutable struct; do not make this readonly
540             /// <summary>Captured ExecutionContext with which to invoke <see cref="MoveNextAction"/>; may be null.</summary>
541             public ExecutionContext Context;
542
543             /// <summary>A delegate to the <see cref="MoveNext"/> method.</summary>
544             public Action MoveNextAction =>
545                 _moveNextAction ??
546                 (_moveNextAction = AsyncCausalityTracer.LoggingOn ? AsyncMethodBuilderCore.OutputAsyncCausalityEvents(this, new Action(MoveNext)) : new Action(MoveNext));
547
548             /// <summary>Calls MoveNext on <see cref="StateMachine"/></summary>
549             public void MoveNext()
550             {
551                 if (Context == null)
552                 {
553                     StateMachine.MoveNext();
554                 }
555                 else
556                 {
557                     ExecutionContext.Run(Context, s_callback, this);
558                 }
559
560                 // In case this is a state machine box with a finalizer, suppress its finalization
561                 // if it's now complete.  We only need the finalizer to run if the box is collected
562                 // without having been completed.
563                 if (IsCompleted && AsyncMethodBuilderCore.TrackAsyncMethodCompletion)
564                 {
565                     GC.SuppressFinalize(this);
566                 }
567             }
568
569             /// <summary>
570             /// Calls MoveNext on <see cref="StateMachine"/>. Implements ITaskCompletionAction.Invoke so
571             /// that the state machine object may be queued directly as a continuation into a Task's
572             /// continuation slot/list.
573             /// </summary>
574             /// <param name="completedTask">The completing task that caused this method to be invoked, if there was one.</param>
575             void ITaskCompletionAction.Invoke(Task completedTask) => MoveNext();
576
577             /// <summary>Signals to Task's continuation logic that <see cref="Invoke"/> runs arbitrary user code via MoveNext.</summary>
578             bool ITaskCompletionAction.InvokeMayRunArbitraryCode => true;
579
580             /// <summary>Gets the state machine as a boxed object.  This should only be used for debugging purposes.</summary>
581             IAsyncStateMachine IAsyncStateMachineBox.GetStateMachineObject() => StateMachine; // likely boxes, only use for debugging
582         }
583
584         /// <summary>Gets the <see cref="System.Threading.Tasks.Task{TResult}"/> for this builder.</summary>
585         /// <returns>The <see cref="System.Threading.Tasks.Task{TResult}"/> representing the builder's asynchronous operation.</returns>
586         public Task<TResult> Task
587         {
588             [MethodImpl(MethodImplOptions.AggressiveInlining)]
589             get => m_task ?? InitializeTaskAsPromise();
590         }
591
592         /// <summary>
593         /// Initializes the task, which must not yet be initialized.  Used only when the Task is being forced into
594         /// existence when no state machine is needed, e.g. when the builder is being synchronously completed with
595         /// an exception, when the builder is being used out of the context of an async method, etc.
596         /// </summary>
597         [MethodImpl(MethodImplOptions.NoInlining)]
598         private Task<TResult> InitializeTaskAsPromise()
599         {
600             Debug.Assert(m_task == null);
601             return (m_task = new Task<TResult>());
602         }
603
604         /// <summary>
605         /// Initializes the task, which must not yet be initialized.  Used only when the Task is being forced into
606         /// existence due to the debugger trying to enable step-out/step-over/etc. prior to the first await yielding
607         /// in an async method.  In that case, we don't know the actual TStateMachine type, so we're forced to
608         /// use IAsyncStateMachine instead.
609         /// </summary>
610         [MethodImpl(MethodImplOptions.NoInlining)]
611         private Task<TResult> InitializeTaskAsStateMachineBox()
612         {
613             Debug.Assert(m_task == null);
614             return (m_task = AsyncMethodBuilderCore.TrackAsyncMethodCompletion ?
615                 new DebugFinalizableAsyncStateMachineBox<IAsyncStateMachine>() :
616                 new AsyncStateMachineBox<IAsyncStateMachine>());
617         }
618
619         /// <summary>
620         /// Completes the <see cref="System.Threading.Tasks.Task{TResult}"/> in the 
621         /// <see cref="System.Threading.Tasks.TaskStatus">RanToCompletion</see> state with the specified result.
622         /// </summary>
623         /// <param name="result">The result to use to complete the task.</param>
624         /// <exception cref="System.InvalidOperationException">The task has already completed.</exception>
625         public void SetResult(TResult result)
626         {
627             // Get the currently stored task, which will be non-null if get_Task has already been accessed.
628             // If there isn't one, get a task and store it.
629             if (m_task == null)
630             {
631                 m_task = GetTaskForResult(result);
632                 Debug.Assert(m_task != null, $"{nameof(GetTaskForResult)} should never return null");
633             }
634             else
635             {
636                 // Slow path: complete the existing task.
637                 SetExistingTaskResult(result);
638             }
639         }
640
641         /// <summary>Completes the already initialized task with the specified result.</summary>
642         /// <param name="result">The result to use to complete the task.</param>
643         private void SetExistingTaskResult(TResult result)
644         {
645             Debug.Assert(m_task != null, "Expected non-null task");
646
647             if (AsyncCausalityTracer.LoggingOn || System.Threading.Tasks.Task.s_asyncDebuggingEnabled)
648             {
649                 LogExistingTaskCompletion();
650             }
651
652             if (!m_task.TrySetResult(result))
653             {
654                 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted);
655             }
656         }
657
658         /// <summary>Handles logging for the successful completion of an operation.</summary>
659         private void LogExistingTaskCompletion()
660         {
661             Debug.Assert(m_task != null);
662
663             if (AsyncCausalityTracer.LoggingOn)
664             {
665                 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, m_task.Id, AsyncCausalityStatus.Completed);
666             }
667
668             // only log if we have a real task that was previously created
669             if (System.Threading.Tasks.Task.s_asyncDebuggingEnabled)
670             {
671                 System.Threading.Tasks.Task.RemoveFromActiveTasks(m_task.Id);
672             }
673         }
674
675         /// <summary>
676         /// Completes the builder by using either the supplied completed task, or by completing
677         /// the builder's previously accessed task using default(TResult).
678         /// </summary>
679         /// <param name="completedTask">A task already completed with the value default(TResult).</param>
680         /// <exception cref="System.InvalidOperationException">The task has already completed.</exception>
681         internal void SetResult(Task<TResult> completedTask)
682         {
683             Debug.Assert(completedTask != null, "Expected non-null task");
684             Debug.Assert(completedTask.IsCompletedSuccessfully, "Expected a successfully completed task");
685
686             // Get the currently stored task, which will be non-null if get_Task has already been accessed.
687             // If there isn't one, store the supplied completed task.
688             if (m_task == null)
689             {
690                 m_task = completedTask;
691             }
692             else
693             {
694                 // Otherwise, complete the task that's there.
695                 SetExistingTaskResult(default(TResult));
696             }
697         }
698
699         /// <summary>
700         /// Completes the <see cref="System.Threading.Tasks.Task{TResult}"/> in the 
701         /// <see cref="System.Threading.Tasks.TaskStatus">Faulted</see> state with the specified exception.
702         /// </summary>
703         /// <param name="exception">The <see cref="System.Exception"/> to use to fault the task.</param>
704         /// <exception cref="System.ArgumentNullException">The <paramref name="exception"/> argument is null (Nothing in Visual Basic).</exception>
705         /// <exception cref="System.InvalidOperationException">The task has already completed.</exception>
706         public void SetException(Exception exception)
707         {
708             if (exception == null)
709             {
710                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception);
711             }
712
713             // Get the task, forcing initialization if it hasn't already been initialized.
714             Task<TResult> task = this.Task;
715
716             // If the exception represents cancellation, cancel the task.  Otherwise, fault the task.
717             var oce = exception as OperationCanceledException;
718             bool successfullySet = oce != null ?
719                 task.TrySetCanceled(oce.CancellationToken, oce) :
720                 task.TrySetException(exception);
721
722             // Unlike with TaskCompletionSource, we do not need to spin here until _taskAndStateMachine is completed,
723             // since AsyncTaskMethodBuilder.SetException should not be immediately followed by any code
724             // that depends on the task having completely completed.  Moreover, with correct usage, 
725             // SetResult or SetException should only be called once, so the Try* methods should always
726             // return true, so no spinning would be necessary anyway (the spinning in TCS is only relevant
727             // if another thread completes the task first).
728             if (!successfullySet)
729             {
730                 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted);
731             }
732         }
733
734         /// <summary>
735         /// Called by the debugger to request notification when the first wait operation
736         /// (await, Wait, Result, etc.) on this builder's task completes.
737         /// </summary>
738         /// <param name="enabled">
739         /// true to enable notification; false to disable a previously set notification.
740         /// </param>
741         /// <remarks>
742         /// This should only be invoked from within an asynchronous method,
743         /// and only by the debugger.
744         /// </remarks>
745         internal void SetNotificationForWaitCompletion(bool enabled)
746         {
747             // Get the task (forcing initialization if not already initialized), and set debug notification
748             (m_task ?? InitializeTaskAsStateMachineBox()).SetNotificationForWaitCompletion(enabled);
749
750             // NOTE: It's important that the debugger use builder.SetNotificationForWaitCompletion
751             // rather than builder.Task.SetNotificationForWaitCompletion.  Even though the latter will
752             // lazily-initialize the task as well, it'll initialize it to a Task<T> (which is important
753             // to minimize size for cases where an ATMB is used directly by user code to avoid the
754             // allocation overhead of a TaskCompletionSource).  If that's done prior to the first await,
755             // the GetMoveNextDelegate code, which needs an AsyncStateMachineBox, will end up creating
756             // a new box and overwriting the previously created task.  That'll change the object identity
757             // of the task being used for wait completion notification, and no notification will
758             // ever arrive, breaking step-out behavior when stepping out before the first yielding await.
759         }
760
761         /// <summary>
762         /// Gets an object that may be used to uniquely identify this builder to the debugger.
763         /// </summary>
764         /// <remarks>
765         /// This property lazily instantiates the ID in a non-thread-safe manner.  
766         /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner
767         /// when no other threads are in the middle of accessing this or other members that lazily initialize the task.
768         /// </remarks>
769         internal object ObjectIdForDebugger => m_task ?? InitializeTaskAsStateMachineBox();
770
771         /// <summary>
772         /// Gets a task for the specified result.  This will either
773         /// be a cached or new task, never null.
774         /// </summary>
775         /// <param name="result">The result for which we need a task.</param>
776         /// <returns>The completed task containing the result.</returns>
777         [MethodImpl(MethodImplOptions.AggressiveInlining)] // method looks long, but for a given TResult it results in a relatively small amount of asm
778         internal static Task<TResult> GetTaskForResult(TResult result)
779         {
780             // The goal of this function is to be give back a cached task if possible,
781             // or to otherwise give back a new task.  To give back a cached task,
782             // we need to be able to evaluate the incoming result value, and we need
783             // to avoid as much overhead as possible when doing so, as this function
784             // is invoked as part of the return path from every async method.
785             // Most tasks won't be cached, and thus we need the checks for those that are 
786             // to be as close to free as possible. This requires some trickiness given the 
787             // lack of generic specialization in .NET.
788             //
789             // Be very careful when modifying this code.  It has been tuned
790             // to comply with patterns recognized by both 32-bit and 64-bit JITs.
791             // If changes are made here, be sure to look at the generated assembly, as
792             // small tweaks can have big consequences for what does and doesn't get optimized away.
793             //
794             // Note that this code only ever accesses a static field when it knows it'll
795             // find a cached value, since static fields (even if readonly and integral types) 
796             // require special access helpers in this NGEN'd and domain-neutral.
797
798             if (null != (object)default(TResult)) // help the JIT avoid the value type branches for ref types
799             {
800                 // Special case simple value types:
801                 // - Boolean
802                 // - Byte, SByte
803                 // - Char
804                 // - Decimal
805                 // - Int32, UInt32
806                 // - Int64, UInt64
807                 // - Int16, UInt16
808                 // - IntPtr, UIntPtr
809                 // As of .NET 4.5, the (Type)(object)result pattern used below
810                 // is recognized and optimized by both 32-bit and 64-bit JITs.
811
812                 // For Boolean, we cache all possible values.
813                 if (typeof(TResult) == typeof(Boolean)) // only the relevant branches are kept for each value-type generic instantiation
814                 {
815                     Boolean value = (Boolean)(object)result;
816                     Task<Boolean> task = value ? AsyncTaskCache.TrueTask : AsyncTaskCache.FalseTask;
817                     return Unsafe.As<Task<TResult>>(task); // UnsafeCast avoids type check we know will succeed
818                 }
819                 // For Int32, we cache a range of common values, e.g. [-1,4).
820                 else if (typeof(TResult) == typeof(Int32))
821                 {
822                     // Compare to constants to avoid static field access if outside of cached range.
823                     // We compare to the upper bound first, as we're more likely to cache miss on the upper side than on the 
824                     // lower side, due to positive values being more common than negative as return values.
825                     Int32 value = (Int32)(object)result;
826                     if (value < AsyncTaskCache.EXCLUSIVE_INT32_MAX &&
827                         value >= AsyncTaskCache.INCLUSIVE_INT32_MIN)
828                     {
829                         Task<Int32> task = AsyncTaskCache.Int32Tasks[value - AsyncTaskCache.INCLUSIVE_INT32_MIN];
830                         return Unsafe.As<Task<TResult>>(task); // UnsafeCast avoids a type check we know will succeed
831                     }
832                 }
833                 // For other known value types, we only special-case 0 / default(TResult).
834                 else if (
835                     (typeof(TResult) == typeof(UInt32) && default(UInt32) == (UInt32)(object)result) ||
836                     (typeof(TResult) == typeof(Byte) && default(Byte) == (Byte)(object)result) ||
837                     (typeof(TResult) == typeof(SByte) && default(SByte) == (SByte)(object)result) ||
838                     (typeof(TResult) == typeof(Char) && default(Char) == (Char)(object)result) ||
839                     (typeof(TResult) == typeof(Decimal) && default(Decimal) == (Decimal)(object)result) ||
840                     (typeof(TResult) == typeof(Int64) && default(Int64) == (Int64)(object)result) ||
841                     (typeof(TResult) == typeof(UInt64) && default(UInt64) == (UInt64)(object)result) ||
842                     (typeof(TResult) == typeof(Int16) && default(Int16) == (Int16)(object)result) ||
843                     (typeof(TResult) == typeof(UInt16) && default(UInt16) == (UInt16)(object)result) ||
844                     (typeof(TResult) == typeof(IntPtr) && default(IntPtr) == (IntPtr)(object)result) ||
845                     (typeof(TResult) == typeof(UIntPtr) && default(UIntPtr) == (UIntPtr)(object)result))
846                 {
847                     return s_defaultResultTask;
848                 }
849             }
850             else if (result == null) // optimized away for value types
851             {
852                 return s_defaultResultTask;
853             }
854
855             // No cached task is available.  Manufacture a new one for this result.
856             return new Task<TResult>(result);
857         }
858     }
859
860     /// <summary>Provides a cache of closed generic tasks for async methods.</summary>
861     internal static class AsyncTaskCache
862     {
863         // All static members are initialized inline to ensure type is beforefieldinit
864
865         /// <summary>A cached Task{Boolean}.Result == true.</summary>
866         internal readonly static Task<Boolean> TrueTask = CreateCacheableTask(true);
867         /// <summary>A cached Task{Boolean}.Result == false.</summary>
868         internal readonly static Task<Boolean> FalseTask = CreateCacheableTask(false);
869
870         /// <summary>The cache of Task{Int32}.</summary>
871         internal readonly static Task<Int32>[] Int32Tasks = CreateInt32Tasks();
872         /// <summary>The minimum value, inclusive, for which we want a cached task.</summary>
873         internal const Int32 INCLUSIVE_INT32_MIN = -1;
874         /// <summary>The maximum value, exclusive, for which we want a cached task.</summary>
875         internal const Int32 EXCLUSIVE_INT32_MAX = 9;
876         /// <summary>Creates an array of cached tasks for the values in the range [INCLUSIVE_MIN,EXCLUSIVE_MAX).</summary>
877         private static Task<Int32>[] CreateInt32Tasks()
878         {
879             Debug.Assert(EXCLUSIVE_INT32_MAX >= INCLUSIVE_INT32_MIN, "Expected max to be at least min");
880             var tasks = new Task<Int32>[EXCLUSIVE_INT32_MAX - INCLUSIVE_INT32_MIN];
881             for (int i = 0; i < tasks.Length; i++)
882             {
883                 tasks[i] = CreateCacheableTask(i + INCLUSIVE_INT32_MIN);
884             }
885             return tasks;
886         }
887
888         /// <summary>Creates a non-disposable task.</summary>
889         /// <typeparam name="TResult">Specifies the result type.</typeparam>
890         /// <param name="result">The result for the task.</param>
891         /// <returns>The cacheable task.</returns>
892         internal static Task<TResult> CreateCacheableTask<TResult>(TResult result) =>
893             new Task<TResult>(false, result, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken));
894     }
895
896     /// <summary>
897     /// An interface implemented by all <see cref="AsyncStateMachineBox{TStateMachine, TResult}"/> instances, regardless of generics.
898     /// </summary>
899     internal interface IAsyncStateMachineBox : ITaskCompletionAction
900     {
901         /// <summary>
902         /// Gets an action for moving forward the contained state machine.
903         /// This will lazily-allocate the delegate as needed.
904         /// </summary>
905         Action MoveNextAction { get; }
906
907         /// <summary>Gets the state machine as a boxed object.  This should only be used for debugging purposes.</summary>
908         IAsyncStateMachine GetStateMachineObject();
909     }
910
911     /// <summary>Shared helpers for manipulating state related to async state machines.</summary>
912     internal static class AsyncMethodBuilderCore // debugger depends on this exact name
913     {
914         /// <summary>Gets whether we should be tracking async method completions for eventing.</summary>
915         internal static bool TrackAsyncMethodCompletion
916         {
917             [MethodImpl(MethodImplOptions.AggressiveInlining)]
918             get => TplEtwProvider.Log.IsEnabled(EventLevel.Warning, TplEtwProvider.Keywords.AsyncMethod);
919         }
920
921         /// <summary>Gets a description of the state of the state machine object, suitable for debug purposes.</summary>
922         /// <param name="stateMachine">The state machine object.</param>
923         /// <returns>A description of the state machine.</returns>
924         internal static string GetAsyncStateMachineDescription(IAsyncStateMachine stateMachine)
925         {
926             Debug.Assert(stateMachine != null);
927
928             Type stateMachineType = stateMachine.GetType();
929             FieldInfo[] fields = stateMachineType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
930
931             var sb = new StringBuilder();
932             sb.AppendLine(stateMachineType.FullName);
933             foreach (FieldInfo fi in fields)
934             {
935                 sb.AppendLine($"    {fi.Name}: {fi.GetValue(stateMachine)}");
936             }
937             return sb.ToString();
938         }
939
940         internal static Action OutputAsyncCausalityEvents(Task task, Action continuation) =>
941             CreateContinuationWrapper(continuation, (innerContinuation, innerTask) =>
942             {
943                 AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, innerTask.Id, CausalitySynchronousWork.Execution);
944                 innerContinuation.Invoke(); // Invoke the original continuation
945                 AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.Execution);
946             }, task);
947
948         internal static Action CreateContinuationWrapper(Action continuation, Action<Action,Task> invokeAction, Task innerTask) =>
949             new ContinuationWrapper(continuation, invokeAction, innerTask).Invoke;
950
951         internal static Action TryGetStateMachineForDebugger(Action action) // debugger depends on this exact name/signature
952         {
953             object target = action.Target;
954             return
955                 target is IAsyncStateMachineBox sm ? sm.GetStateMachineObject().MoveNext :
956                 target is ContinuationWrapper cw ? TryGetStateMachineForDebugger(cw._continuation) :
957                 action;
958         }
959
960         internal static Task TryGetContinuationTask(Action continuation) =>
961             (continuation?.Target as ContinuationWrapper)?._innerTask;
962
963         /// <summary>Throws the exception on the ThreadPool.</summary>
964         /// <param name="exception">The exception to propagate.</param>
965         /// <param name="targetContext">The target context on which to propagate the exception.  Null to use the ThreadPool.</param>
966         internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext)
967         {
968             // Capture the exception into an ExceptionDispatchInfo so that its 
969             // stack trace and Watson bucket info will be preserved
970             var edi = ExceptionDispatchInfo.Capture(exception);
971
972             // If the user supplied a SynchronizationContext...
973             if (targetContext != null)
974             {
975                 try
976                 {
977                     // Post the throwing of the exception to that context, and return.
978                     targetContext.Post(state => ((ExceptionDispatchInfo)state).Throw(), edi);
979                     return;
980                 }
981                 catch (Exception postException)
982                 {
983                     // If something goes horribly wrong in the Post, we'll 
984                     // propagate both exceptions on the ThreadPool
985                     edi = ExceptionDispatchInfo.Capture(new AggregateException(exception, postException));
986                 }
987             }
988
989             // If we have the new error reporting APIs, report this error.  Otherwise, Propagate the exception(s) on the ThreadPool
990 #if FEATURE_COMINTEROP
991             if (!WindowsRuntimeMarshal.ReportUnhandledError(edi.SourceException))
992 #endif // FEATURE_COMINTEROP
993             {
994                 ThreadPool.QueueUserWorkItem(state => ((ExceptionDispatchInfo)state).Throw(), edi);
995             }
996         }
997
998         /// <summary>
999         /// Logically we pass just an Action (delegate) to a task for its action to 'ContinueWith' when it completes.
1000         /// However debuggers and profilers need more information about what that action is. (In particular what 
1001         /// the action after that is and after that.   To solve this problem we create a 'ContinuationWrapper 
1002         /// which when invoked just does the original action (the invoke action), but also remembers other information
1003         /// (like the action after that (which is also a ContinuationWrapper and thus form a linked list).  
1004         //  We also store that task if the action is associate with at task.  
1005         /// </summary>
1006         private sealed class ContinuationWrapper
1007         {
1008             private readonly Action<Action, Task> _invokeAction; // This wrapper is an action that wraps another action, this is that Action.  
1009             internal readonly Action _continuation;              // This is continuation which will happen after m_invokeAction  (and is probably a ContinuationWrapper)
1010             internal readonly Task _innerTask;                   // If the continuation is logically going to invoke a task, this is that task (may be null)
1011
1012             internal ContinuationWrapper(Action continuation, Action<Action, Task> invokeAction, Task innerTask)
1013             {
1014                 Debug.Assert(continuation != null, "Expected non-null continuation");
1015                 Debug.Assert(invokeAction != null, "Expected non-null continuation");
1016
1017                 _invokeAction = invokeAction;
1018                 _continuation = continuation;
1019                 _innerTask = innerTask ?? TryGetContinuationTask(continuation); // if we don't have a task, see if our continuation is a wrapper and use that.
1020             }
1021
1022             internal void Invoke() => _invokeAction(_continuation, _innerTask);
1023         }
1024     }
1025 }