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