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