/// <summary>A delegate to the <see cref="MoveNext"/> method.</summary>
public Action MoveNextAction => _moveNextAction ?? (_moveNextAction = new Action(MoveNext));
+ /// <summary>Invokes <see cref="MoveNext"/> when the box is queued to the thread pool and executed as a work item.</summary>
+ void IThreadPoolWorkItem.ExecuteWorkItem() => MoveNext();
+ void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ }
+
/// <summary>Calls MoveNext on <see cref="StateMachine"/></summary>
public void MoveNext()
{
/// <summary>
/// An interface implemented by all <see cref="AsyncStateMachineBox{TStateMachine, TResult}"/> instances, regardless of generics.
/// </summary>
- internal interface IAsyncStateMachineBox
+ internal interface IAsyncStateMachineBox : IThreadPoolWorkItem
{
/// <summary>Move the state machine forward.</summary>
void MoveNext();
// If we're not allowed to run here, schedule the action
if (!allowInlining || !IsValidLocationForInlining)
{
- UnsafeScheduleAction(box.MoveNextAction, prevCurrentTask);
+ // If logging is disabled, we can simply queue the box itself as a custom work
+ // item, and its work item execution will just invoke its MoveNext. However, if
+ // logging is enabled, there is pre/post-work we need to do around logging to
+ // match what's done for other continuations, and that requires flowing additional
+ // information into the continuation, which we don't want to burden other cases of the
+ // box with... so, in that case we just delegate to the AwaitTaskContinuation-based
+ // path that already handles this, albeit at the expense of allocating the ATC
+ // object, and potentially forcing the box's delegate into existence, when logging
+ // is enabled.
+ if (TplEtwProvider.Log.IsEnabled())
+ {
+ UnsafeScheduleAction(box.MoveNextAction, prevCurrentTask);
+ }
+ else
+ {
+ ThreadPool.UnsafeQueueCustomWorkItem(box, forceGlobal: false);
+ }
return;
}