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