예제 #1
0
        /// <summary>
        /// Gets the Action to use with an awaiter's OnCompleted or UnsafeOnCompleted method.
        /// On first invocation, the supplied state machine will be boxed.
        /// </summary>
        /// <typeparam name="TMethodBuilder">Specifies the type of the method builder used.</typeparam>
        /// <typeparam name="TStateMachine">Specifies the type of the state machine used.</typeparam>
        /// <param name="builder">The builder.</param>
        /// <param name="stateMachine">The state machine.</param>
        /// <returns>An Action to provide to the awaiter.</returns>
#if !SILVERLIGHT
        // [SecuritySafeCritical]
#endif
        internal Action GetCompletionAction <TMethodBuilder, TStateMachine>(
            ref TMethodBuilder builder, ref TStateMachine stateMachine)
            where TMethodBuilder : IAsyncMethodBuilder
            where TStateMachine : IAsyncStateMachine
        {
            // The builder needs to flow ExecutionContext, so capture it.
            var capturedContext = ExecutionContextLightup.Instance.Capture();

            MoveNextRunner runner = new MoveNextRunner(capturedContext);
            Action         action = new Action(runner.Run);

            // If this is our first await, such that we've not yet boxed the state machine, do so now.
            if (m_stateMachine == null)
            {
                // This is our first await, and we're not boxed yet.  First performance any work that
                // must affect both the non-boxed and boxed builders.
                builder.PreBoxInitialization();

                // Box the state machine, then tell the boxed instance to call back into its own builder,
                // so we can cache the boxed reference.
                m_stateMachine = (IAsyncStateMachine)stateMachine;
                m_stateMachine.SetStateMachine(m_stateMachine);
            }

            // Now that we have the state machine, store it into the runner that the action delegate points to.
            // And return the action.
            runner.m_stateMachine = m_stateMachine; // only after this line is the Action delegate usable
            return(action);
        }
        /// <summary>
        /// First await handler. Boxes the <paramref name="stateMachine"/> and initializes continuation action.
        /// </summary>
        /// <typeparam name="TStateMachine">Type of the state machine instance.</typeparam>
        /// <param name="stateMachine">The parent state machine.</param>
        private void OnFirstAwait <TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
        {
            // MoveNextRunner acts as a heap-allocated shell of the state machine + task.
            var runner = new MoveNextRunner <TStateMachine>();

            // Init all members references so they are shared between this instance and the boxed one.
            _continuation = runner.Run;
            _op           = new AsyncResult(AsyncOperationStatus.Running);

            // Copy the state machine into the runner (boxing). This should be done after _continuation and _op is initialized.
            runner.SetStateMachine(ref stateMachine);
        }
        internal Action GetCompletionAction <TMethodBuilder, TStateMachine>(ref TMethodBuilder builder, ref TStateMachine stateMachine)
            where TMethodBuilder : IAsyncMethodBuilder
            where TStateMachine : IAsyncStateMachine
        {
            var    moveNextRunner = new MoveNextRunner(ExecutionContext.Capture());
            Action action         = moveNextRunner.Run;

            if (_stateMachine == null)
            {
                builder.PreBoxInitialization();
                _stateMachine = stateMachine;
                _stateMachine.SetStateMachine(_stateMachine);
            }
            moveNextRunner._stateMachine = _stateMachine;
            return(action);
        }
예제 #4
0
        internal void PostBoxInitialization <TResult>(IAsyncStateMachine stateMachine, MoveNextRunner runner, Task builtTask, TaskCompletionSource <TResult> tcs)
        {
            if (builtTask != null)
            {
            }

            m_stateMachine = stateMachine;
            m_stateMachine.SetStateMachine(m_stateMachine);

            ((IBuilderStateMachine <TResult>)m_stateMachine).SetBuilderTcs(tcs);

            Contract.Assert(runner.m_stateMachine == null, "The runner's state machine should not yet have been populated.");
            Contract.Assert(m_stateMachine != null, "The builder's state machine field should have been initialized.");

            // Now that we have the state machine, store it into the runner that the action delegate points to.
            // And return the action.
            runner.m_stateMachine = m_stateMachine; // only after this line is the Action delegate usable
        }
예제 #5
0
        internal Action GetCompletionAction(Task taskForTracing, ref MoveNextRunner runnerToInitialize)
        {
            Contract.Assert(m_defaultContextAction == null || m_stateMachine != null,
                            "Expected non-null m_stateMachine on non-null m_defaultContextAction");

            // 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();

            // The builder needs to flow ExecutionContext, so capture it.
            var capturedContext = ExecutionContext.Capture(); //.FastCapture(); // ok to use FastCapture as we haven't made any permission demands/asserts

            // If the ExecutionContext is the default context, try to use a cached delegate, creating one if necessary.
            Action         action;
            MoveNextRunner runner;

            if (capturedContext != null) // && capturedContext.IsPreAllocatedDefault)
            {
                // Get the cached delegate, and if it's non-null, return it.
                action = m_defaultContextAction;
                if (action != null)
                {
                    Contract.Assert(m_stateMachine != null, "If the delegate was set, the state machine should have been as well.");
                    return(action);
                }

                // There wasn't a cached delegate, so create one and cache it.
                // The delegate won't be usable until we set the MoveNextRunner's target state machine.
                runner = new MoveNextRunner(capturedContext, m_stateMachine);

                action = new Action(runner.Run);
                if (taskForTracing != null)
                {
                    m_defaultContextAction = action = OutputAsyncCausalityEvents(taskForTracing, action);
                }
                else
                {
                    m_defaultContextAction = action;
                }
            }
            // Otherwise, create an Action that flows this context.  The context may be null.
            // The delegate won't be usable until we set the MoveNextRunner's target state machine.
            else
            {
                runner = new MoveNextRunner(capturedContext, m_stateMachine);
                action = new Action(runner.Run);

                if (taskForTracing != null)
                {
                    action = OutputAsyncCausalityEvents(taskForTracing, action);
                }

                // NOTE: If capturedContext is null, we could create the Action to point directly
                // to m_stateMachine.MoveNext.  However, that follows a much more expensive
                // delegate creation path.
            }

            if (m_stateMachine == null)
            {
                runnerToInitialize = runner;
            }

            return(action);
        }