Reduce Execution Context Save+Restore (#15629)
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Runtime / CompilerServices / AsyncMethodBuilder.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 using System;
6 using System.Diagnostics;
7 using System.Threading;
8
9 namespace System.Runtime.CompilerServices
10 {
11     internal static partial class AsyncMethodBuilder
12     {
13         /// <summary>Initiates the builder's execution with the associated state machine.</summary>
14         /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
15         /// <param name="stateMachine">The state machine instance, passed by reference.</param>
16         [DebuggerStepThrough]
17         public static void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
18         {
19             if (stateMachine == null) // TStateMachines are generally non-nullable value types, so this check will be elided
20             {
21                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine);
22             }
23
24             // enregistrer variables with 0 post-fix so they can be used in registers without EH forcing them to stack
25             // Capture references to Thread Contexts
26             Thread currentThread0 = Thread.CurrentThread;
27             Thread currentThread = currentThread0;
28             ExecutionContext previousExecutionCtx0 = currentThread0.ExecutionContext;
29
30             // Store current ExecutionContext and SynchronizationContext as "previousXxx".
31             // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
32             // so that they won't "leak" out of the first await.
33             ExecutionContext previousExecutionCtx = previousExecutionCtx0;
34             SynchronizationContext previousSyncCtx = currentThread0.SynchronizationContext;
35
36             try
37             {
38                 stateMachine.MoveNext();
39             }
40             finally
41             {
42                 // Re-enregistrer variables post EH with 1 post-fix so they can be used in registers rather than from stack
43                 SynchronizationContext previousSyncCtx1 = previousSyncCtx;
44                 Thread currentThread1 = currentThread;
45                 // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
46                 if (previousSyncCtx1 != currentThread1.SynchronizationContext)
47                 {
48                     // Restore changed SynchronizationContext back to previous
49                     currentThread1.SynchronizationContext = previousSyncCtx1;
50                 }
51
52                 ExecutionContext previousExecutionCtx1 = previousExecutionCtx;
53                 ExecutionContext currentExecutionCtx1 = currentThread1.ExecutionContext;
54                 if (previousExecutionCtx1 != currentExecutionCtx1)
55                 {
56                     // Restore changed ExecutionContext back to previous
57                     currentThread1.ExecutionContext = previousExecutionCtx1;
58                     if ((currentExecutionCtx1 != null && currentExecutionCtx1.HasChangeNotifications) ||
59                         (previousExecutionCtx1 != null && previousExecutionCtx1.HasChangeNotifications))
60                     {
61                         // There are change notifications; trigger any affected
62                         ExecutionContext.OnValuesChanged(currentExecutionCtx1, previousExecutionCtx1);
63                     }
64                 }
65             }
66         }
67     }
68 }