Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -544,12 +544,16 @@ private class AsyncStateMachineBox<TStateMachine> : // SOS DumpAsync command dep
where TStateMachine : IAsyncStateMachine
{
/// <summary>Delegate used to invoke on an ExecutionContext when passed an instance of this box type.</summary>
private static readonly ContextCallback s_callback = s =>
private static readonly ContextCallback s_callback = ExecutionContextCallback;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried to move the lambda to the body? This forces static ctor which looks like is not a common path here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This forces static ctor which looks like is not a common path here.

This callback is common path. Unless you've explicitly suppressed ExecutionContext flow with ExecutionContext.SuppressFlow(), this callback will be used every time the async state machine resumes after an await.

Copy link
Member

@jkotas jkotas Jan 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, before-field-init static ctors are executed lazily in CoreCLR so they pose no problem there. IIRC, it is different in Mono - before-field-init static ctors are eager in Mono that makes use of the before-field-init as a startup and static footprint optimization ineffective.


// Used to initialize s_callback above. We don't use a lambda for this on purpose: a lambda would
// introduce a new generic type behind the scenes that comes with a hefty size penalty in AOT builds.
private static void ExecutionContextCallback(object s)
{
Debug.Assert(s is AsyncStateMachineBox<TStateMachine>);
// Only used privately to pass directly to EC.Run
Unsafe.As<AsyncStateMachineBox<TStateMachine>>(s).StateMachine.MoveNext();
};
}

/// <summary>A delegate to the <see cref="MoveNext()"/> method.</summary>
private Action _moveNextAction;
Expand Down
15 changes: 10 additions & 5 deletions src/System.Private.CoreLib/shared/System/Threading/Tasks/Future.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,16 @@ public class Task<TResult> : Task

private static readonly TaskFactory<TResult> s_Factory = new TaskFactory<TResult>();

// Delegate used by:
// public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks);
// public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks);
// Used to "cast" from Task<Task> to Task<Task<TResult>>.
internal static readonly Func<Task<Task>, Task<TResult>> TaskWhenAnyCast = completed => (Task<TResult>)completed.Result;
// Extract rarely used helper for a static method in a separate type so that the Func<Task<Task>, Task<TResult>>
// generic instantiations don't contribute to all Task instantiations, but only those where WhenAny is used.
internal static class TaskWhenAnyCast
{
// Delegate used by:
// public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks);
// public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks);
// Used to "cast" from Task<Task> to Task<Task<TResult>>.
internal static readonly Func<Task<Task>, Task<TResult>> Value = completed => (Task<TResult>)completed.Result;
}

// Construct a promise-style task without any options.
internal Task() :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6156,7 +6156,7 @@ public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks)
Task<Task> intermediate = WhenAny((Task[])tasks);

// Return a continuation task with the correct result type
return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default,
return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast.Value, default,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
}

Expand Down Expand Up @@ -6185,7 +6185,7 @@ public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> ta
Task<Task> intermediate = WhenAny((IEnumerable<Task>)tasks);

// Return a continuation task with the correct result type
return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default,
return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast.Value, default,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
}
#endregion
Expand Down