Exemplo n.º 1
0
 public void AwaitOnCompleted <TAwaiter, TStateMachine>(
     ref TAwaiter awaiter, ref TStateMachine stateMachine)
     where TAwaiter : INotifyCompletion
     where TStateMachine : IAsyncStateMachine
 {
     EnsureHasTask();
     StateMachineBox <TStateMachine> .AwaitOnCompleted(ref awaiter, ref stateMachine);
 }
        /// <summary>Gets the "boxed" state machine object.</summary>
        /// <typeparam name="TStateMachine">Specifies the type of the async state machine.</typeparam>
        /// <param name="stateMachine">The state machine.</param>
        /// <param name="boxFieldRef">A reference to the field containing the initialized state machine box.</param>
        /// <returns>The "boxed" state machine.</returns>
        private static IAsyncStateMachineBox GetStateMachineBox <TStateMachine>(
            ref TStateMachine stateMachine,
            [NotNull] ref StateMachineBox?boxFieldRef)
            where TStateMachine : IAsyncStateMachine
        {
            ExecutionContext?currentContext = ExecutionContext.Capture();

            // Check first for the most common case: not the first yield in an async method.
            // In this case, the first yield will have already "boxed" the state machine in
            // a strongly-typed manner into an AsyncStateMachineBox.  It will already contain
            // the state machine as well as a MoveNextDelegate and a context.  The only thing
            // we might need to do is update the context if that's changed since it was stored.
            if (boxFieldRef is StateMachineBox <TStateMachine> stronglyTypedBox)
            {
                if (stronglyTypedBox.Context != currentContext)
                {
                    stronglyTypedBox.Context = currentContext;
                }

                return(stronglyTypedBox);
            }

            // The least common case: we have a weakly-typed boxed.  This results if the debugger
            // or some other use of reflection accesses a property like ObjectIdForDebugger.  In
            // such situations, we need to get an object to represent the builder, but we don't yet
            // know the type of the state machine, and thus can't use TStateMachine.  Instead, we
            // use the IAsyncStateMachine interface, which all TStateMachines implement.  This will
            // result in a boxing allocation when storing the TStateMachine if it's a struct, but
            // this only happens in active debugging scenarios where such performance impact doesn't
            // matter.
            if (boxFieldRef is StateMachineBox <IAsyncStateMachine> weaklyTypedBox)
            {
                // If this is the first await, we won't yet have a state machine, so store it.
                if (weaklyTypedBox.StateMachine is null)
                {
                    Debugger.NotifyOfCrossThreadDependency(); // same explanation as with usage below
                    weaklyTypedBox.StateMachine = stateMachine;
                }

                // Update the context.  This only happens with a debugger, so no need to spend
                // extra IL checking for equality before doing the assignment.
                weaklyTypedBox.Context = currentContext;
                return(weaklyTypedBox);
            }

            // Alert a listening debugger that we can't make forward progress unless it slips threads.
            // If we don't do this, and a method that uses "await foo;" is invoked through funceval,
            // we could end up hooking up a callback to push forward the async method's state machine,
            // the debugger would then abort the funceval after it takes too long, and then continuing
            // execution could result in another callback being hooked up.  At that point we have
            // multiple callbacks registered to push the state machine, which could result in bad behavior.
            Debugger.NotifyOfCrossThreadDependency();

            // At this point, m_task should really be null, in which case we want to create the box.
            // However, in a variety of debugger-related (erroneous) situations, it might be non-null,
            // e.g. if the Task property is examined in a Watch window, forcing it to be lazily-intialized
            // as a Task<TResult> rather than as an ValueTaskStateMachineBox.  The worst that happens in such
            // cases is we lose the ability to properly step in the debugger, as the debugger uses that
            // object's identity to track this specific builder/state machine.  As such, we proceed to
            // overwrite whatever's there anyway, even if it's non-null.
            StateMachineBox <TStateMachine> box = StateMachineBox <TStateMachine> .RentFromCache();

            boxFieldRef      = box; // important: this must be done before storing stateMachine into box.StateMachine!
            box.StateMachine = stateMachine;
            box.Context      = currentContext;

            return(box);
        }
 public void AwaitUnsafeOnCompleted <TAwaiter, TStateMachine>(
     ref TAwaiter awaiter, ref TStateMachine stateMachine)
     where TAwaiter : ICriticalNotifyCompletion
     where TStateMachine : IAsyncStateMachine
 => StateMachineBox <TStateMachine> .AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);