Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to remove ExecutionContext and Thread allocations when using SocketAsyncEventArgs?

If you profile a simple client application that uses SocketAsyncEventArgs, you will notice Thread and ExecutionContext allocations.

The source of the allocations is SocketAsyncEventArgs.StartOperationCommon that creates a copy of the ExecutionContext with ExecutionContext.CreateCopy().

ExecutionContext.SuppressFlow seems like a good way to suppress this allocation. However this method itself will generate allocations when ran in a new thread.

How can I avoid these allocations?

like image 294
cao Avatar asked Aug 12 '14 17:08

cao


1 Answers

  1. SocketAsyncEventArgs

    public class SocketAsyncEventArgs : EventArgs, IDisposable { //... // Method called to prepare for a native async socket call. // This method performs the tasks common to all socket operations. internal void StartOperationCommon(Socket socket) {      //...      // Prepare execution context for callback.      if (ExecutionContext.IsFlowSuppressed()) {         // This condition is what you need to pass.          // Fast path for when flow is suppressed.          m_Context = null;         m_ContextCopy = null;      } else {          // Flow is not suppressed.          //...          // If there is an execution context we need          //a fresh copy for each completion.          if(m_Context != null) {             m_ContextCopy = m_Context.CreateCopy();         }     }      // Remember current socket.     m_CurrentSocket = socket;    }         [Pure]     public static bool IsFlowSuppressed()     {         return  Thread.CurrentThread.GetExecutionContextReader().IsFlowSuppressed;     }    //...     } 
  2. ExecutionContext

    [Serializable]  public sealed class ExecutionContext : IDisposable, ISerializable { //... // Misc state variables. private ExecutionContext m_Context; private ExecutionContext m_ContextCopy; private ContextCallback m_ExecutionCallback; //...  internal struct Reader {     ExecutionContext m_ec;     //...      public bool IsFlowSuppressed      {      #if !FEATURE_CORECLR         [MethodImpl(MethodImplOptions.AggressiveInlining)]      #endif         get { return IsNull ? false : m_ec.isFlowSuppressed; }      }    } //end of Reader   internal bool isFlowSuppressed     {      get      {          return (_flags & Flags.IsFlowSuppressed) != Flags.None;      }     set     {         Contract.Assert(!IsPreAllocatedDefault);         if (value)             _flags |= Flags.IsFlowSuppressed;         else             _flags &= ~Flags.IsFlowSuppressed;     }    }   [System.Security.SecurityCritical]  // auto-generated_required public static AsyncFlowControl SuppressFlow() {     if (IsFlowSuppressed())     {         throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotSupressFlowMultipleTimes"));     }     Contract.EndContractBlock();     AsyncFlowControl afc = new AsyncFlowControl();     afc.Setup();     return afc; } //... }//end of ExecutionContext. 
  3. AsyncFlowControl

    public struct AsyncFlowControl: IDisposable { private bool useEC; private ExecutionContext _ec;  //...   [SecurityCritical] internal void Setup() {     useEC = true;     Thread currentThread = Thread.CurrentThread;     _ec = currentThread.GetMutableExecutionContext();     _ec.isFlowSuppressed = true;     _thread = currentThread; } } 
  4. Thread

    // deliberately not [serializable] [ClassInterface(ClassInterfaceType.None)] [ComDefaultInterface(typeof(_Thread))] [System.Runtime.InteropServices.ComVisible(true)] public sealed class Thread : CriticalFinalizerObject, _Thread {   //...   [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]     internal ExecutionContext.Reader GetExecutionContextReader()     {         return new ExecutionContext.Reader(m_ExecutionContext);     } } 

The only way to set isFlowSuppressed to true, to pass the condition in the StartOperationCommon method, is by calling Setup method, and the only call to Setup is in SuppressFlow method, wich you have discussed.

As you can see, SuppressFlow is the only solution.

like image 177
TiyebM Avatar answered Sep 18 '22 17:09

TiyebM