The Labs \ Source Viewer \ SSCLI \ System.Net \ LazyAsyncResult

  1. //------------------------------------------------------------------------------
  2. // <copyright file="_LazyAsyncResult.cs" company="Microsoft">
  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. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Net
  16. {
  17.     using System.Threading;
  18.     using System.Diagnostics;
  19.     using System.Collections;
  20.    
  21.     // LazyAsyncResult - Base class for all IAsyncResult classes
  22.     // that want to take advantage of lazy allocated event handles
  23.     internal class LazyAsyncResult : IAsyncResult
  24.     {
  25.         private const int c_HighBit = unchecked((int)2147483648u);
  26.         private const int c_ForceAsyncCount = 50;
  27.        
  28.         // This is to avoid user mistakes when they queue another async op from a callback the completes sync.
  29.         [ThreadStatic()]
  30.         private static ThreadContext t_ThreadContext;
  31.        
  32.         private static ThreadContext CurrentThreadContext {
  33.             get {
  34.                 ThreadContext threadContext = t_ThreadContext;
  35.                 if (threadContext == null) {
  36.                     threadContext = new ThreadContext();
  37.                     t_ThreadContext = threadContext;
  38.                 }
  39.                 return threadContext;
  40.             }
  41.         }
  42.        
  43.         private class ThreadContext
  44.         {
  45.             internal int m_NestedIOCount;
  46.         }
  47.        
  48.         #if DEBUG
  49.         internal object _DebugAsyncChain;
  50.         private bool _ProtectState;
  51.         // Used by ContextAwareResult to prevent some calls.
  52.         #endif
  53.        
  54.         //
  55.         // class members
  56.         //
  57.         private object m_AsyncObject;
  58.         // Caller's async object.
  59.         private object m_AsyncState;
  60.         // Caller's state object.
  61.         private AsyncCallback m_AsyncCallback;
  62.         // Caller's callback method.
  63.         private object m_Result;
  64.         // Final IO result to be returned byt the End*() method.
  65.         private int m_ErrorCode;
  66.         // Win32 error code for Win32 IO async calls (that want to throw).
  67.         private int m_IntCompleted;
  68.         // Sign bit indicates synchronous completion if set.
  69.         // Remaining bits count the number of InvokeCallbak() calls.
  70.         private bool m_EndCalled;
  71.         // true if the user called the End*() method.
  72.         private bool m_UserEvent;
  73.         // true if the event has been (or is about to be) handed to the user
  74.         private object m_Event;
  75.         // lazy allocated event to be returned in the IAsyncResult for the client to wait on
  76.        
  77.         internal LazyAsyncResult(object myObject, object myState, AsyncCallback myCallBack)
  78.         {
  79.             m_AsyncObject = myObject;
  80.             m_AsyncState = myState;
  81.             m_AsyncCallback = myCallBack;
  82.             m_Result = DBNull.Value;
  83.             GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::.ctor()");
  84.            
  85.         }
  86.        
  87.         // Allows creating a pre-completed result with less interlockeds. Beware! Constructor calls the callback.
  88.         // if a derived class ever uses this and overloads Cleanup, this may need to change
  89.         internal LazyAsyncResult(object myObject, object myState, AsyncCallback myCallBack, object result)
  90.         {
  91.             GlobalLog.Assert(result != DBNull.Value, "LazyAsyncResult#{0}::.ctor()|Result can't be set to DBNull - it's a special internal value.", ValidationHelper.HashString(this));
  92.             m_AsyncObject = myObject;
  93.             m_AsyncState = myState;
  94.             m_AsyncCallback = myCallBack;
  95.             m_Result = result;
  96.             m_IntCompleted = 1;
  97.            
  98.             if (m_AsyncCallback != null) {
  99.                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::Complete() invoking callback");
  100.                 m_AsyncCallback(this);
  101.             }
  102.             else {
  103.                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::Complete() no callback to invoke");
  104.             }
  105.            
  106.             GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::.ctor() (pre-completed)");
  107.         }
  108.        
  109.         // Interface method to return the original async object:
  110.         internal object AsyncObject {
  111.             get { return m_AsyncObject; }
  112.         }
  113.        
  114.         // Interface method to return the caller's state object.
  115.         public object AsyncState {
  116.             get { return m_AsyncState; }
  117.         }
  118.        
  119.         protected AsyncCallback AsyncCallback {
  120.             get { return m_AsyncCallback; }
  121.            
  122.             set { m_AsyncCallback = value; }
  123.         }
  124.        
  125.         // Interface property to return a WaitHandle that can be waited on for I/O completion.
  126.         // This property implements lazy event creation.
  127.         // the event object is only created when this property is accessed,
  128.         // since we're internally only using callbacks, as long as the user is using
  129.         // callbacks as well we will not create an event at all.
  130.         // If this is used, the event cannot be disposed because it is under the control of the
  131.         // application. Internal should use InternalWaitForCompletion instead - never AsyncWaitHandle.
  132.         public WaitHandle AsyncWaitHandle {
  133.             get {
  134.                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_AsyncWaitHandle()");
  135.                
  136.                 #if DEBUG
  137.                 // Can't be called when state is protected.
  138.                 if (_ProtectState) {
  139.                     throw new InvalidOperationException("get_AsyncWaitHandle called in protected state");
  140.                 }
  141.                 #endif
  142.                
  143.                 ManualResetEvent asyncEvent;
  144.                
  145.                 // Indicates that the user has seen the event; it can't be disposed.
  146.                 m_UserEvent = true;
  147.                
  148.                 // The user has access to this object. Lock-in CompletedSynchronously.
  149.                 if (m_IntCompleted == 0) {
  150.                     Interlocked.CompareExchange(ref m_IntCompleted, c_HighBit, 0);
  151.                 }
  152.                
  153.                 // Because InternalWaitForCompletion() tries to dispose this event, it's
  154.                 // possible for m_Event to become null immediately after being set, but only if
  155.                 // IsCompleted has become true. Therefore it's possible for this property
  156.                 // to give different (set) events to different callers when IsCompleted is true.
  157.                 asyncEvent = (ManualResetEvent)m_Event;
  158.                 while (asyncEvent == null) {
  159.                     LazilyCreateEvent(out asyncEvent);
  160.                 }
  161.                
  162.                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_AsyncWaitHandle() m_Event:" + ValidationHelper.HashString(m_Event));
  163.                 return asyncEvent;
  164.             }
  165.         }
  166.        
  167.         // Returns true if this call created the event.
  168.         // May return with a null handle. That means it thought it got one, but it was disposed in the mean time.
  169.         private bool LazilyCreateEvent(out ManualResetEvent waitHandle)
  170.         {
  171.             // lazy allocation of the event:
  172.             // if this property is never accessed this object is never created
  173.             waitHandle = new ManualResetEvent(false);
  174.             try {
  175.                 if (Interlocked.CompareExchange(ref m_Event, waitHandle, null) == null) {
  176.                     if (InternalPeekCompleted) {
  177.                         waitHandle.Set();
  178.                     }
  179.                     return true;
  180.                 }
  181.                 else {
  182.                     waitHandle.Close();
  183.                     waitHandle = (ManualResetEvent)m_Event;
  184.                     // There's a chance here that m_Event became null. But the only way is if another thread completed
  185.                     // in InternalWaitForCompletion and disposed it. If we're in InternalWaitForCompletion, we now know
  186.                     // IsCompleted is set, so we can avoid the wait when waitHandle comes back null. AsyncWaitHandle
  187.                     // will try again in this case.
  188.                     return false;
  189.                 }
  190.             }
  191.             catch {
  192.                 // This should be very rare, but doing this will reduce the chance of deadlock.
  193.                 m_Event = null;
  194.                 if (waitHandle != null)
  195.                     waitHandle.Close();
  196.                 throw;
  197.             }
  198.         }
  199.        
  200.         // This allows ContextAwareResult to not let anyone trigger the CompletedSynchronously tripwire while the context is being captured.
  201.         [Conditional("DEBUG")]
  202.         protected void DebugProtectState(bool protect)
  203.         {
  204.             #if DEBUG
  205.             _ProtectState = protect;
  206.             #endif
  207.         }
  208.        
  209.         // Interface property, returning synchronous completion status.
  210.         public bool CompletedSynchronously {
  211.             get {
  212.                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_CompletedSynchronously()");
  213.                
  214.                 #if DEBUG
  215.                 // Can't be called when state is protected.
  216.                 if (_ProtectState) {
  217.                     throw new InvalidOperationException("get_CompletedSynchronously called in protected state");
  218.                 }
  219.                 #endif
  220.                
  221.                 // If this returns greater than zero, it means it was incremented by InvokeCallback before anyone ever saw it.
  222.                 int result = m_IntCompleted;
  223.                 if (result == 0) {
  224.                     result = Interlocked.CompareExchange(ref m_IntCompleted, c_HighBit, 0);
  225.                 }
  226.                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_CompletedSynchronously() returns: " + ((result > 0) ? "true" : "false"));
  227.                 return result > 0;
  228.             }
  229.         }
  230.        
  231.         // Interface property, returning completion status.
  232.         public bool IsCompleted {
  233.             get {
  234.                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_IsCompleted()");
  235.                
  236.                 #if DEBUG
  237.                 // Can't be called when state is protected.
  238.                 if (_ProtectState) {
  239.                     throw new InvalidOperationException("get_IsCompleted called in protected state");
  240.                 }
  241.                 #endif
  242.                
  243.                 // Look at just the low bits to see if it's been incremented. If it hasn't, set the high bit
  244.                 // to show that it's been looked at.
  245.                 int result = m_IntCompleted;
  246.                 if (result == 0) {
  247.                     result = Interlocked.CompareExchange(ref m_IntCompleted, c_HighBit, 0);
  248.                 }
  249.                 return (result & ~c_HighBit) != 0;
  250.             }
  251.         }
  252.        
  253.         // Use to see if something's completed without fixing CompletedSynchronously
  254.         internal bool InternalPeekCompleted {
  255.             get { return (m_IntCompleted & ~c_HighBit) != 0; }
  256.         }
  257.        
  258.         // Internal property for setting the IO result.
  259.         internal object Result {
  260.             get { return m_Result == DBNull.Value ? null : m_Result; }
  261.             set {
  262.                 // Ideally this should never be called, since setting
  263.                 // the result object really makes sense when the IO completes.
  264.                 //
  265.                 // But if the result was set here (as a preemptive error or for some other reason),
  266.                 // then the "result" parameter passed to InvokeCallback() will be ignored.
  267.                 //
  268.                
  269.                 // It's an error to call after the result has been completed or with DBNull.
  270.                 GlobalLog.Assert(value != DBNull.Value, "LazyAsyncResult#{0}::set_Result()|Result can't be set to DBNull - it's a special internal value.", ValidationHelper.HashString(this));
  271.                 GlobalLog.Assert(!InternalPeekCompleted, "LazyAsyncResult#{0}::set_Result()|Called on completed result.", ValidationHelper.HashString(this));
  272.                 m_Result = value;
  273.             }
  274.         }
  275.        
  276.         internal bool EndCalled {
  277.             get { return m_EndCalled; }
  278.             set { m_EndCalled = value; }
  279.         }
  280.        
  281.         // Internal property for setting the Win32 IO async error code.
  282.         internal int ErrorCode {
  283.             get { return m_ErrorCode; }
  284.             set { m_ErrorCode = value; }
  285.         }
  286.        
  287.         // A method for completing the IO with a result
  288.         // and invoking the user's callback.
  289.         // Used by derived classes to pass context into an overridden Complete(). Useful
  290.         // for determining the 'winning' thread in case several may simultaneously call
  291.         // the equivalent of InvokeCallback().
  292.         protected void ProtectedInvokeCallback(object result, IntPtr userToken)
  293.         {
  294.             GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::ProtectedInvokeCallback() result = " + (result is Exception ? ((Exception)result).Message : result == null ? "<null>" : result.ToString()) + ", userToken:" + userToken.ToString());
  295.            
  296.             // Critical to disallow DBNull here - it could result in a stuck spinlock in WaitForCompletion.
  297.             if (result == DBNull.Value) {
  298.                 throw new ArgumentNullException("result");
  299.             }
  300.            
  301.             #if DEBUG
  302.             // Always safe to ask for the state now.
  303.             _ProtectState = false;
  304.             #endif
  305.            
  306.             if ((m_IntCompleted & ~c_HighBit) == 0 && (Interlocked.Increment(ref m_IntCompleted) & ~c_HighBit) == 1) {
  307.                 // DBNull.Value is used to guarantee that the first caller wins,
  308.                 // even if the result was set to null.
  309.                 if (m_Result == DBNull.Value)
  310.                     m_Result = result;
  311.                
  312.                 // Does this need a memory barrier to be sure this thread gets the m_Event if it's set? I don't think so
  313.                 // because the Interlockeds on m_IntCompleted/m_Event should serve as the barrier.
  314.                 ManualResetEvent asyncEvent = (ManualResetEvent)m_Event;
  315.                 if (asyncEvent != null) {
  316.                     asyncEvent.Set();
  317.                 }
  318.                
  319.                 Complete(userToken);
  320.             }
  321.         }
  322.        
  323.         // A method for completing the IO with a result
  324.         // and invoking the user's callback.
  325.         internal void InvokeCallback(object result)
  326.         {
  327.             ProtectedInvokeCallback(result, IntPtr.Zero);
  328.         }
  329.        
  330.         // A method for completing the IO without a result
  331.         // and invoking the user's callback.
  332.         internal void InvokeCallback()
  333.         {
  334.             ProtectedInvokeCallback(null, IntPtr.Zero);
  335.         }
  336.        
  337.         //
  338.         // MUST NOT BE CALLED DIRECTLY
  339.         // A protected method that does callback job and it is guaranteed to be called exactly once.
  340.         // A derived overriding method must call the base class somewhere or the completion is lost.
  341.         //
  342.         protected virtual void Complete(IntPtr userToken)
  343.         {
  344.             bool offloaded = false;
  345.             ThreadContext threadContext = CurrentThreadContext;
  346.             try {
  347.                 ++threadContext.m_NestedIOCount;
  348.                 if (m_AsyncCallback != null) {
  349.                     GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::Complete() invoking callback");
  350.                    
  351.                     if (threadContext.m_NestedIOCount >= c_ForceAsyncCount) {
  352.                         GlobalLog.Print("LazyAsyncResult::Complete *** OFFLOADED the user callback ***");
  353.                         ThreadPool.QueueUserWorkItem(new WaitCallback(WorkerThreadComplete));
  354.                         offloaded = true;
  355.                     }
  356.                     else {
  357.                         m_AsyncCallback(this);
  358.                     }
  359.                 }
  360.                 else {
  361.                     GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::Complete() no callback to invoke");
  362.                 }
  363.             }
  364.             finally {
  365.                 --threadContext.m_NestedIOCount;
  366.                
  367.                 // Never call this method unless interlocked m_IntCompleted check has succeeded (like in this case)
  368.                 if (!offloaded) {
  369.                     Cleanup();
  370.                 }
  371.             }
  372.         }
  373.        
  374.        
  375.        
  376.         // Only called in the above method
  377.         void WorkerThreadComplete(object state)
  378.         {
  379.             try {
  380.                 m_AsyncCallback(this);
  381.             }
  382.             finally {
  383.                 Cleanup();
  384.             }
  385.         }
  386.        
  387.         // Custom instance cleanup method.
  388.         // Derived types override this method to release unmanaged resources associated with an IO request.
  389.         protected virtual void Cleanup()
  390.         {
  391.         }
  392.        
  393.         internal object InternalWaitForCompletion()
  394.         {
  395.             return WaitForCompletion(true);
  396.         }
  397.        
  398. /*
  399.         internal object InternalWaitForCompletionNoSideEffects()
  400.         {
  401.             return WaitForCompletion(false);
  402.         }
  403.         */       
  404.        
  405.         private object WaitForCompletion(bool snap)
  406.         {
  407.             ManualResetEvent waitHandle = null;
  408.             bool createdByMe = false;
  409.             bool complete = snap ? IsCompleted : InternalPeekCompleted;
  410.            
  411.             if (!complete) {
  412.                 // Not done yet, so wait:
  413.                 waitHandle = (ManualResetEvent)m_Event;
  414.                 if (waitHandle == null) {
  415.                     createdByMe = LazilyCreateEvent(out waitHandle);
  416.                 }
  417.             }
  418.            
  419.             if (waitHandle != null) {
  420.                 try {
  421.                     GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::InternalWaitForCompletion() Waiting for completion m_Event#" + ValidationHelper.HashString(waitHandle));
  422.                     waitHandle.WaitOne(Timeout.Infinite, false);
  423.                 }
  424.                 catch (ObjectDisposedException) {
  425.                     // This can occur if this method is called from two different threads.
  426.                     // This possibility is the trade-off for not locking.
  427.                 }
  428.                 finally {
  429.                     // We also want to dispose the event although we can't unless we did wait on it here.
  430.                     if (createdByMe && !m_UserEvent) {
  431.                         // Does m_UserEvent need to be volatile (or m_Event set via Interlocked) in order
  432.                         // to avoid giving a user a disposed event?
  433.                         ManualResetEvent oldEvent = (ManualResetEvent)m_Event;
  434.                         m_Event = null;
  435.                         if (!m_UserEvent) {
  436.                             oldEvent.Close();
  437.                         }
  438.                     }
  439.                 }
  440.             }
  441.            
  442.             // A race condition exists because InvokeCallback sets m_IntCompleted before m_Result (so that m_Result
  443.             // can benefit from the synchronization of m_IntCompleted). That means you can get here before m_Result got
  444.             // set (although rarely - once every eight hours of stress). Handle that case with a spin-lock.
  445.             while (m_Result == DBNull.Value)
  446.                 Thread.SpinWait(1);
  447.            
  448.             GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::InternalWaitForCompletion() done: " + (m_Result is Exception ? ((Exception)m_Result).Message : m_Result == null ? "<null>" : m_Result.ToString()));
  449.            
  450.             return m_Result;
  451.         }
  452.        
  453.         // A general interface that is called to release unmanaged resources associated with the class.
  454.         // It completes the result but doesn't do any of the notifications.
  455.         internal void InternalCleanup()
  456.         {
  457.             if ((m_IntCompleted & ~c_HighBit) == 0 && (Interlocked.Increment(ref m_IntCompleted) & ~c_HighBit) == 1) {
  458.                 // Set no result so that just in case there are waiters, they don't hang in the spin lock.
  459.                 m_Result = null;
  460.                 Cleanup();
  461.             }
  462.         }
  463.     }
  464. }

Developer Fusion