The Labs \ Source Viewer \ SSCLI \ System.Threading \ AsyncFlowControl

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. /*============================================================
  15. **
  16. ** Class:  ExecutionContext
  17. **
  18. **
  19. ** Purpose: Capture execution  context for a thread
  20. **
  21. **
  22. ===========================================================*/
  23. namespace System.Threading
  24. {
  25.     using System;
  26.     using System.Security;
  27.     using System.Runtime.Remoting;
  28.     using System.Security.Principal;
  29.     using System.Collections;
  30.     using System.Reflection;
  31.     using System.Runtime.Serialization;
  32.     using System.Security.Permissions;
  33.     using System.Runtime.Remoting.Contexts;
  34.     using System.Runtime.Remoting.Messaging;
  35.     using System.Runtime.InteropServices;
  36.     using System.Runtime.CompilerServices;
  37.     using System.Runtime.ConstrainedExecution;
  38.    
  39.     internal enum ExceptionType
  40.     {
  41.         InvalidOperation = 0,
  42.         Security = 1,
  43.         EE = 2,
  44.         Generic = 3
  45.     }
  46.     // helper delegate to statically bind to Wait method
  47.     internal delegate int WaitDelegate(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
  48.    
  49.    
  50.     internal struct ExecutionContextSwitcher : IDisposable
  51.     {
  52.         internal ExecutionContext prevEC;
  53.         // previous EC we need to restore on Undo
  54.         internal ExecutionContext currEC;
  55.         // current EC that we store for checking correctness
  56.         internal SecurityContextSwitcher scsw;
  57.         internal SynchronizationContextSwitcher sysw;
  58.         internal object hecsw;
  59.         internal Thread thread;
  60.        
  61.         public override bool Equals(object obj)
  62.         {
  63.             if (obj == null || !(obj is ExecutionContextSwitcher))
  64.                 return false;
  65.             ExecutionContextSwitcher sw = (ExecutionContextSwitcher)obj;
  66.             return (this.prevEC == sw.prevEC && this.currEC == sw.currEC && this.scsw == sw.scsw && this.sysw == sw.sysw && this.hecsw == sw.hecsw && this.thread == sw.thread);
  67.         }
  68.        
  69.         public override int GetHashCode()
  70.         {
  71.             return ToString().GetHashCode();
  72.         }
  73.        
  74.         public static bool operator ==(ExecutionContextSwitcher c1, ExecutionContextSwitcher c2)
  75.         {
  76.             return c1.Equals(c2);
  77.         }
  78.        
  79.         public static bool operator !=(ExecutionContextSwitcher c1, ExecutionContextSwitcher c2)
  80.         {
  81.             return !c1.Equals(c2);
  82.         }
  83.        
  84.         /// <internalonly/>
  85.         void IDisposable.Dispose()
  86.         {
  87.             Undo();
  88.         }
  89.        
  90.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  91.         internal bool UndoNoThrow()
  92.         {
  93.             try {
  94.                 Undo();
  95.             }
  96.             catch {
  97.                 return false;
  98.             }
  99.             return true;
  100.         }
  101.        
  102.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  103.         public void Undo()
  104.         {
  105.             if (thread == null) {
  106.                 return;
  107.                 // Don't do anything
  108.             }
  109.             if (thread != Thread.CurrentThread) {
  110.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotUseSwitcherOtherThread"));
  111.             }
  112.             if (currEC != Thread.CurrentThread.GetExecutionContextNoCreate()) {
  113.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_SwitcherCtxMismatch"));
  114.             }
  115.             BCLDebug.Assert(currEC != null, " ExecutionContext can't be null");
  116.            
  117.            
  118.             // Any critical failure inside scsw will cause FailFast
  119.             scsw.Undo();
  120.            
  121.             try {
  122.                 HostExecutionContextSwitcher.Undo(hecsw);
  123.             }
  124.             finally {
  125.                 // Even if HostExecutionContextSwitcher.Undo(hecsw) throws, we need to revert
  126.                 // synchronizationContext. If that throws, we'll be throwing an ex during an exception
  127.                 // unwind. That's OK - we'll just have nested exceptions.
  128.                 sysw.Undo();
  129.             }
  130.            
  131.             // restore the saved Execution Context
  132.             Thread.CurrentThread.SetExecutionContext(prevEC);
  133.             thread = null;
  134.             // this will prevent the switcher object being used again
  135.            
  136.         }
  137.     }
  138.    
  139.    
  140.     public struct AsyncFlowControl : IDisposable
  141.     {
  142.         private bool useEC;
  143.         private ExecutionContext _ec;
  144.         private SecurityContext _sc;
  145.         private Thread _thread;
  146.         internal void Setup(SecurityContextDisableFlow flags)
  147.         {
  148.             useEC = false;
  149.             _sc = Thread.CurrentThread.ExecutionContext.SecurityContext;
  150.             _sc._disableFlow = flags;
  151.             _thread = Thread.CurrentThread;
  152.         }
  153.         internal void Setup()
  154.         {
  155.             useEC = true;
  156.             _ec = Thread.CurrentThread.ExecutionContext;
  157.             _ec.isFlowSuppressed = true;
  158.             _thread = Thread.CurrentThread;
  159.         }
  160.        
  161.         /// <internalonly/>
  162.         void IDisposable.Dispose()
  163.         {
  164.             Undo();
  165.         }
  166.        
  167.         public void Undo()
  168.         {
  169.             if (_thread == null) {
  170.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotUseAFCMultiple"));
  171.             }
  172.             if (_thread != Thread.CurrentThread) {
  173.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotUseAFCOtherThread"));
  174.             }
  175.             if (useEC) {
  176.                 if (Thread.CurrentThread.ExecutionContext != _ec) {
  177.                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncFlowCtrlCtxMismatch"));
  178.                 }
  179.                 ExecutionContext.RestoreFlow();
  180.             }
  181.             else {
  182.                 if (Thread.CurrentThread.ExecutionContext.SecurityContext != _sc) {
  183.                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncFlowCtrlCtxMismatch"));
  184.                 }
  185.                 SecurityContext.RestoreFlow();
  186.             }
  187.             _thread = null;
  188.         }
  189.        
  190.         public override int GetHashCode()
  191.         {
  192.             return _thread == null ? ToString().GetHashCode() : _thread.GetHashCode();
  193.         }
  194.        
  195.         public override bool Equals(object obj)
  196.         {
  197.             if (obj is AsyncFlowControl)
  198.                 return Equals((AsyncFlowControl)obj);
  199.             else
  200.                 return false;
  201.         }
  202.        
  203.         public bool Equals(AsyncFlowControl obj)
  204.         {
  205.             return obj.useEC == useEC && obj._ec == _ec && obj._sc == _sc && obj._thread == _thread;
  206.         }
  207.        
  208.         public static bool operator ==(AsyncFlowControl a, AsyncFlowControl b)
  209.         {
  210.             return a.Equals(b);
  211.         }
  212.        
  213.         public static bool operator !=(AsyncFlowControl a, AsyncFlowControl b)
  214.         {
  215.             return !(a == b);
  216.         }
  217.        
  218.     }
  219.    
  220.     [System.Runtime.InteropServices.ComVisible(true)]
  221.     public delegate void ContextCallback(object state);
  222.    
  223.    
  224.     [Serializable()]
  225.     public sealed class ExecutionContext : ISerializable
  226.     {
  227. /*=========================================================================
  228.         ** Data accessed from managed code that needs to be defined in
  229.         ** ExecutionContextObject  to maintain alignment between the two classes.
  230.         ** DON'T CHANGE THESE UNLESS YOU MODIFY ExecutionContextObject in vm\object.h
  231.         =========================================================================*/       
  232.         private HostExecutionContext _hostExecutionContext;
  233.         private SynchronizationContext _syncContext;
  234.         private SecurityContext _securityContext;
  235.         private LogicalCallContext _logicalCallContext;
  236.         private IllogicalCallContext _illogicalCallContext;
  237.         // this call context follows the physical thread
  238.         private Thread _thread;
  239.         internal bool isNewCapture = false;
  240.         internal bool isFlowSuppressed = false;
  241.        
  242.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  243.         internal ExecutionContext()
  244.         {
  245.         }
  246.        
  247.         internal LogicalCallContext LogicalCallContext {
  248.             get {
  249.                 if (_logicalCallContext == null) {
  250.                     _logicalCallContext = new LogicalCallContext();
  251.                 }
  252.                 return _logicalCallContext;
  253.             }
  254.             set { _logicalCallContext = value; }
  255.         }
  256.        
  257.         internal IllogicalCallContext IllogicalCallContext {
  258.             get {
  259.                 if (_illogicalCallContext == null) {
  260.                     _illogicalCallContext = new IllogicalCallContext();
  261.                 }
  262.                 return _illogicalCallContext;
  263.             }
  264.             set { _illogicalCallContext = value; }
  265.         }
  266.         internal Thread Thread {
  267.             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  268.             set { _thread = value; }
  269.         }
  270.        
  271.         internal SynchronizationContext SynchronizationContext {
  272.             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  273.             get { return _syncContext; }
  274.             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  275.             set { _syncContext = value; }
  276.         }
  277.        
  278.         internal HostExecutionContext HostExecutionContext {
  279.             get { return _hostExecutionContext; }
  280.             set { _hostExecutionContext = value; }
  281.         }
  282.        
  283.         internal SecurityContext SecurityContext {
  284.             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  285.             get { return _securityContext; }
  286.             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  287.             set {
  288.                 // store the new security context
  289.                 _securityContext = value;
  290.                 // perform the reverse link too
  291.                 if (value != null)
  292.                     _securityContext.ExecutionContext = this;
  293.             }
  294.         }
  295.        
  296.        
  297.         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure), DynamicSecurityMethodAttribute()]
  298.         public static void Run(ExecutionContext executionContext, ContextCallback callback, object state)
  299.         {
  300.             if (executionContext == null) {
  301.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NullContext"));
  302.             }
  303.            
  304.             if (!executionContext.isNewCapture) {
  305.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_NotNewCaptureContext"));
  306.             }
  307.            
  308.             executionContext.isNewCapture = false;
  309.            
  310.             ExecutionContext ec = Thread.CurrentThread.GetExecutionContextNoCreate();
  311.             if ((ec == null || ec.IsDefaultFTContext()) && SecurityContext.CurrentlyInDefaultFTSecurityContext(ec) && executionContext.IsDefaultFTContext()) {
  312.                 callback(state);
  313.             }
  314.             else {
  315.                 RunInternal(executionContext, callback, state);
  316.             }
  317.         }
  318.        
  319.         static internal void RunInternal(ExecutionContext executionContext, ContextCallback callback, object state)
  320.         {
  321.             if (cleanupCode == null) {
  322.                 tryCode = new RuntimeHelpers.TryCode(runTryCode);
  323.                 cleanupCode = new RuntimeHelpers.CleanupCode(runFinallyCode);
  324.             }
  325.            
  326.             ExecutionContextRunData runData = new ExecutionContextRunData(executionContext, callback, state);
  327.             RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(tryCode, cleanupCode, runData);
  328.         }
  329.        
  330.         internal class ExecutionContextRunData
  331.         {
  332.             internal ExecutionContext ec;
  333.             internal ContextCallback callBack;
  334.             internal object state;
  335.             internal ExecutionContextSwitcher ecsw;
  336.             internal ExecutionContextRunData(ExecutionContext executionContext, ContextCallback cb, object state)
  337.             {
  338.                 this.ec = executionContext;
  339.                 this.callBack = cb;
  340.                 this.state = state;
  341.                 ecsw = new ExecutionContextSwitcher();
  342.             }
  343.         }
  344.        
  345.         static internal void runTryCode(object userData)
  346.         {
  347.             ExecutionContextRunData rData = (ExecutionContextRunData)userData;
  348.             rData.ecsw = SetExecutionContext(rData.ec);
  349.             rData.callBack(rData.state);
  350.            
  351.         }
  352.        
  353.         [PrePrepareMethod()]
  354.         static internal void runFinallyCode(object userData, bool exceptionThrown)
  355.         {
  356.             ExecutionContextRunData rData = (ExecutionContextRunData)userData;
  357.             rData.ecsw.Undo();
  358.         }
  359.        
  360.         static internal RuntimeHelpers.TryCode tryCode;
  361.         static internal RuntimeHelpers.CleanupCode cleanupCode;
  362.        
  363.        
  364.         // Sets the given execution context object on the thread.
  365.         // Returns the previous one.
  366.         [DynamicSecurityMethodAttribute()]
  367.         static internal ExecutionContextSwitcher SetExecutionContext(ExecutionContext executionContext)
  368.         {
  369.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  370.             BCLDebug.Assert(executionContext != null, "ExecutionContext cannot be null here.");
  371.            
  372.            
  373.             // Set up the switcher object to return;
  374.             ExecutionContextSwitcher ecsw = new ExecutionContextSwitcher();
  375.            
  376.             ecsw.thread = Thread.CurrentThread;
  377.             ecsw.prevEC = Thread.CurrentThread.GetExecutionContextNoCreate();
  378.             // prev
  379.             ecsw.currEC = executionContext;
  380.             //current
  381.             // Update the EC on thread
  382.             Thread.CurrentThread.SetExecutionContext(executionContext);
  383.            
  384.             RuntimeHelpers.PrepareConstrainedRegions();
  385.             try {
  386.                 if (executionContext != null) {
  387.                     //set the security context
  388.                     SecurityContext sc = executionContext.SecurityContext;
  389.                     if (sc != null) {
  390.                         // non-null SC: needs to be set
  391.                         SecurityContext prevSeC = (ecsw.prevEC != null) ? ecsw.prevEC.SecurityContext : null;
  392.                         ecsw.scsw = SecurityContext.SetSecurityContext(sc, prevSeC, ref stackMark);
  393.                     }
  394.                     else if (!SecurityContext.CurrentlyInDefaultFTSecurityContext(ecsw.prevEC)) {
  395.                         // null incoming SC, but we're currently not in FT: use static FTSC to set
  396.                         SecurityContext prevSeC = (ecsw.prevEC != null) ? ecsw.prevEC.SecurityContext : null;
  397.                         ecsw.scsw = SecurityContext.SetSecurityContext(SecurityContext.FullTrustSecurityContext, prevSeC, ref stackMark);
  398.                     }
  399.                    
  400.                     // set the sync context
  401.                     SynchronizationContext syncContext = executionContext.SynchronizationContext;
  402.                     if (syncContext != null) {
  403.                         SynchronizationContext prevSyC = (ecsw.prevEC != null) ? ecsw.prevEC.SynchronizationContext : null;
  404.                         ecsw.sysw = SynchronizationContext.SetSynchronizationContext(syncContext, prevSyC);
  405.                     }
  406.                    
  407.                     // set the Host Context
  408.                     HostExecutionContext hostContext = executionContext.HostExecutionContext;
  409.                     if (hostContext != null) {
  410.                         ecsw.hecsw = HostExecutionContextManager.SetHostExecutionContextInternal(hostContext);
  411.                     }
  412.                    
  413.                 }
  414.             }
  415.             catch {
  416.                 ecsw.UndoNoThrow();
  417.                 throw;
  418.             }
  419.             return ecsw;
  420.         }
  421.        
  422.         public ExecutionContext CreateCopy()
  423.         {
  424.             if (!isNewCapture) {
  425.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotCopyUsedContext"));
  426.             }
  427.             ExecutionContext ec = new ExecutionContext();
  428.             ec.isNewCapture = true;
  429.             ec._syncContext = _syncContext == null ? null : _syncContext.CreateCopy();
  430.             // capture the host execution context
  431.             ec._hostExecutionContext = _hostExecutionContext == null ? null : _hostExecutionContext.CreateCopy();
  432.             if (_securityContext != null) {
  433.                 ec._securityContext = _securityContext.CreateCopy();
  434.                 ec._securityContext.ExecutionContext = ec;
  435.             }
  436.             if (this._logicalCallContext != null) {
  437.                 LogicalCallContext lc = (LogicalCallContext)this.LogicalCallContext;
  438.                 ec.LogicalCallContext = (LogicalCallContext)lc.Clone();
  439.             }
  440.             if (this._illogicalCallContext != null) {
  441.                 IllogicalCallContext ilcc = (IllogicalCallContext)this.IllogicalCallContext;
  442.                 ec.IllogicalCallContext = (IllogicalCallContext)ilcc.Clone();
  443.             }
  444.            
  445.             return ec;
  446.         }
  447.        
  448.         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
  449.         public static AsyncFlowControl SuppressFlow()
  450.         {
  451.             if (IsFlowSuppressed()) {
  452.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotSupressFlowMultipleTimes"));
  453.             }
  454.             AsyncFlowControl afc = new AsyncFlowControl();
  455.             afc.Setup();
  456.             return afc;
  457.         }
  458.        
  459.         public static void RestoreFlow()
  460.         {
  461.             ExecutionContext ec = Thread.CurrentThread.GetExecutionContextNoCreate();
  462.             if (ec == null || !ec.isFlowSuppressed) {
  463.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotRestoreUnsupressedFlow"));
  464.             }
  465.             ec.isFlowSuppressed = false;
  466.         }
  467.        
  468.         public static bool IsFlowSuppressed()
  469.         {
  470.             ExecutionContext ec = Thread.CurrentThread.GetExecutionContextNoCreate();
  471.             if (ec == null)
  472.                 return false;
  473.             else
  474.                 return ec.isFlowSuppressed;
  475.         }
  476.        
  477.         public static ExecutionContext Capture()
  478.         {
  479.             // set up a stack mark for finding the caller
  480.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  481.             return ExecutionContext.Capture(ref stackMark);
  482.         }
  483.        
  484.         // internal helper to capture the current execution context using a passed in stack mark
  485.         static internal ExecutionContext Capture(ref StackCrawlMark stackMark)
  486.         {
  487.             // check to see if Flow is suppressed
  488.             if (IsFlowSuppressed())
  489.                 return null;
  490.            
  491.             ExecutionContext ecCurrent = Thread.CurrentThread.GetExecutionContextNoCreate();
  492.             ExecutionContext ecNew = new ExecutionContext();
  493.             ecNew.isNewCapture = true;
  494.            
  495.             // capture the security context
  496.             ecNew.SecurityContext = SecurityContext.Capture(ecCurrent, ref stackMark);
  497.             if (ecNew.SecurityContext != null)
  498.                 ecNew.SecurityContext.ExecutionContext = ecNew;
  499.            
  500.             // capture the host execution context
  501.             ecNew._hostExecutionContext = HostExecutionContextManager.CaptureHostExecutionContext();
  502.            
  503.            
  504.             if (ecCurrent != null) {
  505.                 // capture the sync context
  506.                 ecNew._syncContext = (ecCurrent._syncContext == null) ? null : ecCurrent._syncContext.CreateCopy();
  507.                
  508.                 // copy over the Logical Call Context
  509.                 if (ecCurrent._logicalCallContext != null) {
  510.                     LogicalCallContext lc = (LogicalCallContext)ecCurrent.LogicalCallContext;
  511.                     ecNew.LogicalCallContext = (LogicalCallContext)lc.Clone();
  512.                 }
  513.                
  514.             }
  515.             return ecNew;
  516.         }
  517.        
  518.         //
  519.         // Implementation of ISerializable
  520.         //
  521.        
  522.         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
  523.         public void GetObjectData(SerializationInfo info, StreamingContext context)
  524.         {
  525.             if (info == null)
  526.                 throw new ArgumentNullException("info");
  527.            
  528.             if (_logicalCallContext != null) {
  529.                 info.AddValue("LogicalCallContext", _logicalCallContext, typeof(LogicalCallContext));
  530.             }
  531.         }
  532.        
  533.         private ExecutionContext(SerializationInfo info, StreamingContext context)
  534.         {
  535.             SerializationInfoEnumerator e = info.GetEnumerator();
  536.             while (e.MoveNext()) {
  537.                 if (e.Name.Equals("LogicalCallContext")) {
  538.                     _logicalCallContext = (LogicalCallContext)e.Value;
  539.                 }
  540.             }
  541.             this.Thread = Thread.CurrentThread;
  542.         }
  543.         // ObjRef .ctor
  544.         static internal void ClearSyncContext(ExecutionContext ec)
  545.         {
  546.             if (ec != null)
  547.                 ec.SynchronizationContext = null;
  548.         }
  549.         internal bool IsDefaultFTContext()
  550.         {
  551.             if (_hostExecutionContext != null)
  552.                 return false;
  553.             if (_syncContext != null)
  554.                 return false;
  555.             if (_securityContext != null && !_securityContext.IsDefaultFTSecurityContext())
  556.                 return false;
  557.             if (_logicalCallContext != null && _logicalCallContext.HasInfo)
  558.                 return false;
  559.             if (_illogicalCallContext != null && _illogicalCallContext.HasUserData)
  560.                 return false;
  561.             return true;
  562.         }
  563.     }
  564.     // class ExecutionContext
  565. }

Developer Fusion