The Labs \ Source Viewer \ SSCLI \ System.Net.Sockets \ OverlappedCache

  1. //------------------------------------------------------------------------------
  2. // <copyright file="_OverlappedAsyncResult.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.Sockets
  16. {
  17.     using System;
  18.     using System.Net;
  19.     using System.Runtime.InteropServices;
  20.     using System.Threading;
  21.     using Microsoft.Win32;
  22.    
  23.     //
  24.     // BaseOverlappedAsyncResult - used to enable async Socket operation
  25.     // such as the BeginSend, BeginSendTo, BeginReceive, BeginReceiveFrom, BeginSendFile,
  26.     // BeginAccept, calls.
  27.     //
  28.    
  29.     internal class BaseOverlappedAsyncResult : ContextAwareResult
  30.     {
  31.         //
  32.         // internal class members
  33.         //
  34.         private SafeOverlappedFree m_UnmanagedBlob;
  35.         // Handle for global memory.
  36.         private AutoResetEvent m_OverlappedEvent;
  37.         private int m_CleanupCount;
  38.         private bool m_DisableOverlapped;
  39.         private bool m_UseOverlappedIO;
  40.         private GCHandle[] m_GCHandles;
  41.         private OverlappedCache m_Cache;
  42.        
  43.         //
  44.         // The WinNT Completion Port callback.
  45.         //
  46.         unsafe private static readonly IOCompletionCallback s_IOCallback = new IOCompletionCallback(CompletionPortCallback);
  47.        
  48.         //
  49.         // Constructor. We take in the socket that's creating us, the caller's
  50.         // state object, and callback. We save the socket and state, and allocate
  51.         // an event for the WaitHandle.
  52.         //
  53.         internal BaseOverlappedAsyncResult(Socket socket, object asyncState, AsyncCallback asyncCallback) : base(socket, asyncState, asyncCallback)
  54.         {
  55.             //
  56.             // BeginAccept() allocates and returns an AcceptOverlappedAsyncResult that will call
  57.             // this constructor.
  58.             //
  59.             m_UseOverlappedIO = Socket.UseOverlappedIO || socket.UseOnlyOverlappedIO;
  60.             if (m_UseOverlappedIO) {
  61.                 //
  62.                 // since the binding between the event handle and the callback
  63.                 // happens after the IO was queued to the OS, there is no race
  64.                 // condition and the Cleanup code can be called at most once.
  65.                 //
  66.                 m_CleanupCount = 1;
  67.             }
  68.             else {
  69.                 //
  70.                 // since the binding between the event handle and the callback
  71.                 // has already happened there is a race condition and so the
  72.                 // Cleanup code can be called more than once and at most twice.
  73.                 //
  74.                 m_CleanupCount = 2;
  75.             }
  76.         }
  77.        
  78.         //
  79.         // Constructor. We take in the socket that's creating us, and turn off Async
  80.         // We save the socket and state.
  81.         internal BaseOverlappedAsyncResult(Socket socket) : base(socket, null, null)
  82.         {
  83.             m_CleanupCount = 1;
  84.             m_DisableOverlapped = true;
  85.         }
  86.        
  87.        
  88.         //PostCompletion returns the result object to be set before the user's callback is invoked.
  89.         internal virtual object PostCompletion(int numBytes)
  90.         {
  91.             return numBytes;
  92.         }
  93.        
  94.        
  95.         //
  96.         // SetUnmanagedStructures -
  97.         //
  98.         // This needs to be called for overlapped IO to function properly.
  99.         //
  100.         // Fills in Overlapped Structures used in an Async Overlapped Winsock call
  101.         // these calls are outside the runtime and are unmanaged code, so we need
  102.         // to prepare specific structures and ints that lie in unmanaged memory
  103.         // since the Overlapped calls can be Async
  104.         //
  105.         internal void SetUnmanagedStructures(object objectsToPin)
  106.         {
  107.             if (!m_DisableOverlapped) {
  108.                 // Casting to object[] is very expensive. Only do it once.
  109.                 object[] objectsToPinArray = null;
  110.                 bool triedCastingToArray = false;
  111.                
  112.                 bool useCache = false;
  113.                 if (m_Cache != null) {
  114.                     if (objectsToPin == null && m_Cache.PinnedObjects == null) {
  115.                         useCache = true;
  116.                     }
  117.                     else if (m_Cache.PinnedObjects != null) {
  118.                         if (m_Cache.PinnedObjectsArray == null) {
  119.                             if (objectsToPin == m_Cache.PinnedObjects) {
  120.                                 useCache = true;
  121.                             }
  122.                         }
  123.                         else if (objectsToPin != null) {
  124.                             triedCastingToArray = true;
  125.                             objectsToPinArray = objectsToPin as object[];
  126.                             if (objectsToPinArray != null && objectsToPinArray.Length == 0) {
  127.                                 objectsToPinArray = null;
  128.                             }
  129.                             if (objectsToPinArray != null && objectsToPinArray.Length == m_Cache.PinnedObjectsArray.Length) {
  130.                                 useCache = true;
  131.                                 for (int i = 0; i < objectsToPinArray.Length; i++) {
  132.                                     if (objectsToPinArray[i] != m_Cache.PinnedObjectsArray[i]) {
  133.                                         useCache = false;
  134.                                         break;
  135.                                     }
  136.                                 }
  137.                             }
  138.                         }
  139.                     }
  140.                 }
  141.                
  142.                 if (!useCache && m_Cache != null) {
  143.                     GlobalLog.Print("BaseOverlappedAsyncResult#" + ValidationHelper.HashString(this) + "::SetUnmanagedStructures() Cache miss - freeing cache.");
  144.                     m_Cache.Free();
  145.                     m_Cache = null;
  146.                 }
  147.                
  148.                 Socket s = (Socket)AsyncObject;
  149.                 if (m_UseOverlappedIO) {
  150.                     GlobalLog.Assert(m_UnmanagedBlob == null, "BaseOverlappedAsyncResult#{0}::SetUnmanagedStructures()|Unmanaged blob already allocated. (Called twice?)", ValidationHelper.HashString(this));
  151.                     m_UnmanagedBlob = SafeOverlappedFree.Alloc(s.SafeHandle);
  152.                    
  153.                     PinUnmanagedObjects(objectsToPin);
  154.                    
  155.                     //
  156.                     // create the event handle
  157.                     //
  158.                    
  159.                     m_OverlappedEvent = new AutoResetEvent(false);
  160.                    
  161.                     //
  162.                     // fill in the overlapped structure with the event handle.
  163.                     //
  164.                    
  165.                     Marshal.WriteIntPtr(m_UnmanagedBlob.DangerousGetHandle(), Win32.OverlappedhEventOffset, m_OverlappedEvent.SafeWaitHandle.DangerousGetHandle());
  166.                 }
  167.                 else {
  168.                     //
  169.                     // Bind the Win32 Socket Handle to the ThreadPool
  170.                     //
  171.                     s.BindToCompletionPort();
  172.                    
  173.                     if (m_Cache == null) {
  174.                         GlobalLog.Print("BaseOverlappedAsyncResult#" + ValidationHelper.HashString(this) + "::EnableCompletionPort() Creating new overlapped cache.");
  175.                         if (objectsToPinArray != null) {
  176.                             m_Cache = new OverlappedCache(new Overlapped(), objectsToPinArray, s_IOCallback);
  177.                         }
  178.                         else {
  179.                             m_Cache = new OverlappedCache(new Overlapped(), objectsToPin, s_IOCallback, triedCastingToArray);
  180.                         }
  181.                     }
  182.                     else {
  183.                         GlobalLog.Print("BaseOverlappedAsyncResult#" + ValidationHelper.HashString(this) + "::EnableCompletionPort() Using cached overlapped.");
  184.                     }
  185.                    
  186.                     m_Cache.Overlapped.AsyncResult = this;
  187.                    
  188.                     #if DEBUG
  189.                     unsafe {
  190.                         m_InitialNativeOverlapped = *((NativeOverlapped*)m_Cache.NativeOverlapped);
  191.                     }
  192.                     #endif
  193.                    
  194.                     GlobalLog.Print("BaseOverlappedAsyncResult#" + ValidationHelper.HashString(this) + "::EnableCompletionPort() overlapped:" + ValidationHelper.HashString(m_Cache.Overlapped) + " NativeOverlapped = " + m_Cache.NativeOverlapped.ToString("x"));
  195.                 }
  196.             }
  197.         }
  198.        
  199. /*
  200.         //                                   
  201.         internal void SetUnmanagedStructures(object objectsToPin, ref OverlappedCache overlappedCache)
  202.         {
  203.             SetupCache(ref overlappedCache);
  204.             SetUnmanagedStructures(objectsToPin);
  205.         }
  206.         */       
  207.        
  208.         protected void SetupCache(ref OverlappedCache overlappedCache)
  209.         {
  210.             GlobalLog.Assert(m_Cache == null, "BaseOverlappedAsyncResult#{0}::SetUnmanagedStructures()|Cache already set up. (Called twice?)", ValidationHelper.HashString(this));
  211.             if (!m_UseOverlappedIO && !m_DisableOverlapped) {
  212.                 m_Cache = overlappedCache == null ? null : Interlocked.Exchange<OverlappedCache>(ref overlappedCache, null);
  213.                
  214.                 // Need to hold on to the unmanaged structures until the cache is extracted.
  215.                 m_CleanupCount++;
  216.             }
  217.         }
  218.        
  219.         //
  220.         // This method pins unmanaged objects for Win9x and systems where completion ports are not found
  221.         //
  222.         protected void PinUnmanagedObjects(object objectsToPin)
  223.         {
  224.             if (m_Cache != null) {
  225.                 m_Cache.Free();
  226.                 m_Cache = null;
  227.             }
  228.            
  229.             if (objectsToPin != null) {
  230.                 if (objectsToPin.GetType() == typeof(object[])) {
  231.                     object[] objectsArray = (object[])objectsToPin;
  232.                     m_GCHandles = new GCHandle[objectsArray.Length];
  233.                     for (int i = 0; i < objectsArray.Length; i++) {
  234.                         if (objectsArray[i] != null) {
  235.                             m_GCHandles[i] = GCHandle.Alloc(objectsArray[i], GCHandleType.Pinned);
  236.                         }
  237.                     }
  238.                 }
  239.                 else {
  240.                     m_GCHandles = new GCHandle[1];
  241.                     m_GCHandles[0] = GCHandle.Alloc(objectsToPin, GCHandleType.Pinned);
  242.                 }
  243.             }
  244.         }
  245.        
  246.         internal void ExtractCache(ref OverlappedCache overlappedCache)
  247.         {
  248.             if (!m_UseOverlappedIO && !m_DisableOverlapped) {
  249.                 // Have to be super careful. Socket isn't synchronized, so if a user calls End() twice, we don't want to
  250.                 // copy out this cache twice which could result in posting an IO with a deleted NativeOverlapped.
  251.                 OverlappedCache cache = m_Cache == null ? null : Interlocked.Exchange<OverlappedCache>(ref m_Cache, null);
  252.                 if (cache != null) {
  253.                     // If overlappedCache is null, just slap it in there. There's a chance for a conflict,
  254.                     // resulting in a OverlappedCache getting finalized, but it's better than
  255.                     // the interlocked. This won't be an issue in most 'correct' cases.
  256.                     if (overlappedCache == null) {
  257.                         overlappedCache = cache;
  258.                     }
  259.                     else {
  260.                         OverlappedCache oldCache = Interlocked.Exchange<OverlappedCache>(ref overlappedCache, cache);
  261.                         if (oldCache != null) {
  262.                             oldCache.Free();
  263.                         }
  264.                     }
  265.                 }
  266.                
  267.                 ReleaseUnmanagedStructures();
  268.             }
  269.         }
  270.        
  271.         unsafe private static void CompletionPortCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped)
  272.         {
  273.             #if DEBUG
  274.             GlobalLog.SetThreadSource(ThreadKinds.CompletionPort);
  275.             using (GlobalLog.SetThreadKind(ThreadKinds.System)) {
  276.                 #endif
  277.                 //
  278.                 // Create an Overlapped object out of the native pointer we're provided with.
  279.                 // (this will NOT free the unmanaged memory in the native overlapped structure)
  280.                 //
  281.                 Overlapped callbackOverlapped = Overlapped.Unpack(nativeOverlapped);
  282.                 BaseOverlappedAsyncResult asyncResult = (BaseOverlappedAsyncResult)callbackOverlapped.AsyncResult;
  283.                
  284.                 // The AsyncResult must be cleared before the callback is called (i.e. before ExtractCache is called).
  285.                 // Not doing so leads to a leak where the pinned cached OverlappedData continues to point to the async result object,
  286.                 // which points to the Socket (as well as user data), which points to the OverlappedCache, preventing the OverlappedCache
  287.                 // finalizer from freeing the pinned OverlappedData.
  288.                 callbackOverlapped.AsyncResult = null;
  289.                
  290.                 object returnObject = null;
  291.                
  292.                 GlobalLog.Assert(!asyncResult.InternalPeekCompleted, "BaseOverlappedAsyncResult#{0}::CompletionPortCallback()|asyncResult.IsCompleted", ValidationHelper.HashString(asyncResult));
  293.                
  294.                 GlobalLog.Print("BaseOverlappedAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::CompletionPortCallback" + " errorCode:" + errorCode.ToString() + " numBytes:" + numBytes.ToString() + " pOverlapped:" + ((int)nativeOverlapped).ToString());
  295.                
  296.                 //
  297.                 // complete the IO and invoke the user's callback
  298.                 //
  299.                 SocketError socketError = (SocketError)errorCode;
  300.                
  301.                 if (socketError != SocketError.Success && socketError != SocketError.OperationAborted) {
  302.                    
  303.                     Socket socket = asyncResult.AsyncObject as Socket;
  304.                     if (socket == null) {
  305.                         socketError = SocketError.NotSocket;
  306.                     }
  307.                     else if (socket.CleanedUp) {
  308.                         socketError = SocketError.OperationAborted;
  309.                     }
  310.                     else {
  311.                         try {
  312.                             //
  313.                             // The Async IO completed with a failure.
  314.                             // here we need to call WSAGetOverlappedResult() just so Marshal.GetLastWin32Error() will return the correct error.
  315.                             //
  316.                             bool success = UnsafeNclNativeMethods.OSSOCK.WSAGetOverlappedResult(socket.SafeHandle, (IntPtr)nativeOverlapped, out numBytes, false, IntPtr.Zero);
  317.                            
  318.                             if (!success) {
  319.                                 socketError = (SocketError)Marshal.GetLastWin32Error();
  320.                                 GlobalLog.Assert(socketError != 0, "BaseOverlappedAsyncResult#{0}::CompletionPortCallback()|socketError:0 numBytes:{1}", ValidationHelper.HashString(asyncResult), numBytes);
  321.                             }
  322.                            
  323.                             GlobalLog.Assert(!success, "BaseOverlappedAsyncResult#{0}::CompletionPortCallback()|Unexpectedly succeeded. errorCode:{1} numBytes:{2}", ValidationHelper.HashString(asyncResult), errorCode, numBytes);
  324.                         }
  325.                         // CleanedUp check above does not always work since this code is subject to race conditions
  326.                         catch (ObjectDisposedException) {
  327.                             socketError = SocketError.OperationAborted;
  328.                         }
  329.                     }
  330.                 }
  331.                 asyncResult.ErrorCode = (int)socketError;
  332.                 returnObject = asyncResult.PostCompletion((int)numBytes);
  333.                 asyncResult.ReleaseUnmanagedStructures();
  334.                 asyncResult.InvokeCallback(returnObject);
  335.                 #if DEBUG
  336.             }
  337.             #endif
  338.         }
  339.        
  340.        
  341.         //
  342.         // The overlapped function called (either by the thread pool or the socket)
  343.         // when IO completes. (only called on Win9x)
  344.         //
  345.         private void OverlappedCallback(object stateObject, bool Signaled)
  346.         {
  347.             #if DEBUG
  348.             // GlobalLog.SetThreadSource(ThreadKinds.Worker); Because of change 1077887, need logic to determine thread type here.
  349.             using (GlobalLog.SetThreadKind(ThreadKinds.System)) {
  350.                 #endif
  351.                 BaseOverlappedAsyncResult asyncResult = (BaseOverlappedAsyncResult)stateObject;
  352.                
  353.                 GlobalLog.Assert(!asyncResult.InternalPeekCompleted, "AcceptOverlappedAsyncResult#{0}::OverlappedCallback()|asyncResult.IsCompleted", ValidationHelper.HashString(asyncResult));
  354.                 //
  355.                 // the IO completed asynchronously, see if there was a failure the Internal
  356.                 // field in the Overlapped structure will be non zero. to optimize the non
  357.                 // error case, we look at it without calling WSAGetOverlappedResult().
  358.                 //
  359.                 uint errorCode = (uint)Marshal.ReadInt32(IntPtrHelper.Add(asyncResult.m_UnmanagedBlob.DangerousGetHandle(), Win32.OverlappedInternalOffset));
  360.                 uint numBytes = errorCode != 0 ? unchecked((uint)-1) : (uint)Marshal.ReadInt32(IntPtrHelper.Add(asyncResult.m_UnmanagedBlob.DangerousGetHandle(), Win32.OverlappedInternalHighOffset));
  361.                 //
  362.                 // this will release the unmanaged pin handles and unmanaged overlapped ptr
  363.                 //
  364.                 asyncResult.ErrorCode = (int)errorCode;
  365.                 object returnObject = asyncResult.PostCompletion((int)numBytes);
  366.                 asyncResult.ReleaseUnmanagedStructures();
  367.                 asyncResult.InvokeCallback(returnObject);
  368.                 #if DEBUG
  369.             }
  370.             #endif
  371.         }
  372.        
  373.         #if DEBUG
  374.         private SocketError m_SavedErrorCode = unchecked((SocketError)3735928559u);
  375.         private NativeOverlapped m_InitialNativeOverlapped;
  376.         private NativeOverlapped m_IntermediateNativeOverlapped;
  377.         #endif
  378.        
  379.         //
  380.         // This method is called after an asynchronous call is made for the user,
  381.         // it checks and acts accordingly if the IO:
  382.         // 1) completed synchronously.
  383.         // 2) was pended.
  384.         // 3) failed.
  385.         //
  386.         unsafe internal SocketError CheckAsyncCallOverlappedResult(SocketError errorCode)
  387.         {
  388.             #if DEBUG
  389.             m_SavedErrorCode = errorCode;
  390.             #endif
  391.            
  392.             //
  393.             // Check if the Async IO call:
  394.             // 1) was pended.
  395.             // 2) completed synchronously.
  396.             // 3) failed.
  397.             //
  398.             if (m_UseOverlappedIO) {
  399.                 //
  400.                 // we're using overlapped IO under Win9x (or NT with registry setting overriding
  401.                 // completion port usage)
  402.                 //
  403.                 switch (errorCode) {
  404.                     case 0:
  405.                     case SocketError.IOPending:
  406.                        
  407.                        
  408.                         //
  409.                         // the Async IO call was pended:
  410.                         // Queue our event to the thread pool.
  411.                         //
  412.                         GlobalLog.Assert(m_UnmanagedBlob != null, "BaseOverlappedAsyncResult#{0}::CheckAsyncCallOverlappedResult()|Unmanaged blob isn't allocated.", ValidationHelper.HashString(this));
  413.                         ThreadPool.UnsafeRegisterWaitForSingleObject(m_OverlappedEvent, new WaitOrTimerCallback(OverlappedCallback), this, -1, true);
  414.                        
  415.                         //
  416.                         // we're done, completion will be asynchronous
  417.                         // in the callback. return
  418.                         //
  419.                         return SocketError.Success;
  420.                     default:
  421.                        
  422.                         //
  423.                         // the Async IO call failed:
  424.                         // set the number of bytes transferred to -1 (error)
  425.                         //
  426.                         ErrorCode = (int)errorCode;
  427.                         Result = -1;
  428.                         ReleaseUnmanagedStructures();
  429.                         break;
  430.                 }
  431.             }
  432.             else {
  433.                 #if DEBUG
  434.                 OverlappedCache cache = m_Cache;
  435.                 if (cache != null)
  436.                     unsafe {
  437.                         NativeOverlapped* nativeOverlappedPtr = (NativeOverlapped*)cache.NativeOverlapped;
  438.                         if (nativeOverlappedPtr != null)
  439.                             m_IntermediateNativeOverlapped = *nativeOverlappedPtr;
  440.                     }
  441.                 #endif
  442.                 //
  443.                 // We're using completion ports under WinNT. Release one reference on the structures for
  444.                 // the main thread.
  445.                 //
  446.                 ReleaseUnmanagedStructures();
  447.                
  448.                 switch (errorCode) {
  449.                     case 0:
  450.                     case SocketError.IOPending:
  451.                         //
  452.                         // ignore cases in which a completion packet will be queued:
  453.                         // we'll deal with this IO in the callback
  454.                         //
  455.                         //
  456.                         // ignore, do nothing
  457.                         //
  458.                         return SocketError.Success;
  459.                     default:
  460.                        
  461.                         //
  462.                         // in the remaining cases a completion packet will NOT be queued:
  463.                         // we'll have to call the callback explicitly signaling an error
  464.                         //
  465.                         //
  466.                         // call the callback with error code
  467.                         //
  468.                         ErrorCode = (int)errorCode;
  469.                         Result = -1;
  470.                        
  471.                         // The AsyncResult must be cleared since the callback isn't going to be called.
  472.                         // Not doing so leads to a leak where the pinned cached OverlappedData continues to point to the async result object,
  473.                         // which points to the Socket (as well as user data) and to the OverlappedCache, preventing the OverlappedCache
  474.                         // finalizer from freeing the pinned OverlappedData.
  475.                         if (m_Cache != null) {
  476.                             // Could be null only if SetUnmanagedStructures weren't called.
  477.                             m_Cache.Overlapped.AsyncResult = null;
  478.                         }
  479.                        
  480.                         ReleaseUnmanagedStructures();
  481.                         // Additional release for the completion that won't happen.
  482.                         break;
  483.                 }
  484.             }
  485.             return errorCode;
  486.         }
  487.        
  488.         //
  489.         // The following property returns the Win32 unsafe pointer to
  490.         // whichever Overlapped structure we're using for IO.
  491.         //
  492.         unsafe internal IntPtr OverlappedHandle {
  493.             get {
  494.                 if (m_UseOverlappedIO) {
  495.                     //
  496.                     // on Win9x we allocate our own overlapped structure
  497.                     // and we use a win32 event for IO completion
  498.                     // return the native pointer to unmanaged memory
  499.                     //
  500.                     return (m_UnmanagedBlob == null || m_UnmanagedBlob.IsInvalid) ? IntPtr.Zero : m_UnmanagedBlob.DangerousGetHandle();
  501.                 }
  502.                 else {
  503.                     //
  504.                     // on WinNT we need to use (due to the current implementation)
  505.                     // an Overlapped object in order to bind the socket to the
  506.                     // ThreadPool's completion port, so return the native handle
  507.                     //
  508.                     return m_Cache == null ? IntPtr.Zero : m_Cache.NativeOverlapped;
  509.                 }
  510.             }
  511.         }
  512.         // OverlappedHandle
  513.        
  514.         private void ReleaseUnmanagedStructures()
  515.         {
  516.             if (Interlocked.Decrement(ref m_CleanupCount) == 0) {
  517.                 ForceReleaseUnmanagedStructures();
  518.             }
  519.         }
  520.        
  521.         protected override void Cleanup()
  522.         {
  523.             base.Cleanup();
  524.            
  525.             // If we get all the way to here and it's still not cleaned up...
  526.             if (m_CleanupCount > 0 && Interlocked.Exchange(ref m_CleanupCount, 0) > 0) {
  527.                 ForceReleaseUnmanagedStructures();
  528.             }
  529.         }
  530.        
  531.         // Utility cleanup routine. Frees the overlapped structure.
  532.         // This should be overriden to free pinned and unmanaged memory in the subclass.
  533.         // It needs to also be invoked from the subclass.
  534.         protected virtual void ForceReleaseUnmanagedStructures()
  535.         {
  536.             //
  537.             // free the unmanaged memory if allocated.
  538.             //
  539.             ReleaseGCHandles();
  540.             GC.SuppressFinalize(this);
  541.            
  542.             if (m_UnmanagedBlob != null && !m_UnmanagedBlob.IsInvalid) {
  543.                 m_UnmanagedBlob.Close(true);
  544.                 m_UnmanagedBlob = null;
  545.             }
  546.            
  547.             // This is interlocked because Cleanup() can be called simultaneously with ExtractCache().
  548.             OverlappedCache.InterlockedFree(ref m_Cache);
  549.            
  550.             if (m_OverlappedEvent != null) {
  551.                 m_OverlappedEvent.Close();
  552.                 m_OverlappedEvent = null;
  553.             }
  554.         }
  555.        
  556.         ~BaseOverlappedAsyncResult()
  557.         {
  558.             ReleaseGCHandles();
  559.         }
  560.        
  561.         private void ReleaseGCHandles()
  562.         {
  563.             GCHandle[] gcHandles = m_GCHandles;
  564.             if (gcHandles != null) {
  565.                 for (int i = 0; i < gcHandles.Length; i++) {
  566.                     if (gcHandles[i].IsAllocated) {
  567.                         gcHandles[i].Free();
  568.                     }
  569.                 }
  570.             }
  571.         }
  572.     }
  573.    
  574.     internal class OverlappedCache
  575.     {
  576.         internal Overlapped m_Overlapped;
  577.         internal IntPtr m_NativeOverlapped;
  578.         internal object m_PinnedObjects;
  579.         internal object[] m_PinnedObjectsArray;
  580.        
  581.         internal OverlappedCache(Overlapped overlapped, object[] pinnedObjectsArray, IOCompletionCallback callback)
  582.         {
  583.             m_Overlapped = overlapped;
  584.             m_PinnedObjects = pinnedObjectsArray;
  585.             m_PinnedObjectsArray = pinnedObjectsArray;
  586.            
  587.             unsafe {
  588.                 m_NativeOverlapped = (IntPtr)overlapped.UnsafePack(callback, pinnedObjectsArray);
  589.             }
  590.         }
  591.        
  592.         internal OverlappedCache(Overlapped overlapped, object pinnedObjects, IOCompletionCallback callback, bool alreadyTriedCast)
  593.         {
  594.             m_Overlapped = overlapped;
  595.             m_PinnedObjects = pinnedObjects;
  596.             m_PinnedObjectsArray = alreadyTriedCast ? null : NclConstants.EmptyObjectArray;
  597.            
  598.             unsafe {
  599.                 m_NativeOverlapped = (IntPtr)overlapped.UnsafePack(callback, pinnedObjects);
  600.             }
  601.         }
  602.        
  603.         internal Overlapped Overlapped {
  604.             get { return m_Overlapped; }
  605.         }
  606.        
  607.         internal IntPtr NativeOverlapped {
  608.             get { return m_NativeOverlapped; }
  609.         }
  610.        
  611.         internal object PinnedObjects {
  612.             get { return m_PinnedObjects; }
  613.         }
  614.        
  615.         internal object[] PinnedObjectsArray {
  616.             get {
  617.                 object[] pinnedObjectsArray = m_PinnedObjectsArray;
  618.                 if (pinnedObjectsArray != null && pinnedObjectsArray.Length == 0) {
  619.                     pinnedObjectsArray = m_PinnedObjects as object[];
  620.                     if (pinnedObjectsArray != null && pinnedObjectsArray.Length == 0) {
  621.                         m_PinnedObjectsArray = null;
  622.                     }
  623.                     else {
  624.                         m_PinnedObjectsArray = pinnedObjectsArray;
  625.                     }
  626.                 }
  627.                 return m_PinnedObjectsArray;
  628.             }
  629.         }
  630.        
  631.         // This must only be called once.
  632.         internal void Free()
  633.         {
  634.             InternalFree();
  635.             GC.SuppressFinalize(this);
  636.         }
  637.        
  638.         private void InternalFree()
  639.         {
  640.             m_Overlapped = null;
  641.             m_PinnedObjects = null;
  642.            
  643.             if (m_NativeOverlapped != IntPtr.Zero) {
  644.                 unsafe {
  645.                     Overlapped.Free((NativeOverlapped*)m_NativeOverlapped);
  646.                 }
  647.                 m_NativeOverlapped = IntPtr.Zero;
  648.             }
  649.         }
  650.        
  651.         static internal void InterlockedFree(ref OverlappedCache overlappedCache)
  652.         {
  653.             OverlappedCache cache = overlappedCache == null ? null : Interlocked.Exchange<OverlappedCache>(ref overlappedCache, null);
  654.             if (cache != null) {
  655.                 cache.Free();
  656.             }
  657.         }
  658.        
  659.         ~OverlappedCache()
  660.         {
  661.             if (!NclUtilities.HasShutdownStarted) {
  662.                 InternalFree();
  663.             }
  664.         }
  665.     }
  666. }

Developer Fusion