The Labs \ Source Viewer \ SSCLI \ System.Internal \ HandleCollector

  1. //------------------------------------------------------------------------------
  2. // <copyright file="HandleCollector.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. #if DEBUG_HANDLECOLLECTOR
  16. using System.Diagnostics;
  17. #endif
  18. using System.Collections.Generic;
  19. using System.Diagnostics;
  20. using System.Diagnostics.CodeAnalysis;
  21. namespace System.Internal
  22. {
  23.    
  24.     internal sealed class HandleCollector
  25.     {
  26.         private static HandleType[] handleTypes;
  27.         private static int handleTypeCount;
  28.         private static int suspendCount;
  29.        
  30.         static internal event HandleChangeEventHandler HandleAdded;
  31.        
  32.         static internal event HandleChangeEventHandler HandleRemoved;
  33.        
  34.         private static object internalSyncObject = new object();
  35.        
  36.         /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.Add"]/*' />
  37.         /// <devdoc>
  38.         /// Adds the given handle to the handle collector. This keeps the
  39.         /// handle on a "hot list" of objects that may need to be garbage
  40.         /// collected.
  41.         /// </devdoc>
  42.         static internal IntPtr Add(IntPtr handle, int type)
  43.         {
  44.             handleTypes[type - 1].Add(handle);
  45.             return handle;
  46.         }
  47.        
  48.         /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.Add"]/*' />
  49.         /// <devdoc>
  50.         /// Suspends GC.Collect
  51.         /// </devdoc>
  52.         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  53.         static internal void SuspendCollect()
  54.         {
  55.             lock (internalSyncObject) {
  56.                 suspendCount++;
  57.             }
  58.         }
  59.        
  60.         /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.Add"]/*' />
  61.         /// <devdoc>
  62.         /// Resumes GC.Collect
  63.         /// </devdoc>
  64.         [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
  65.         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  66.         static internal void ResumeCollect()
  67.         {
  68.             bool performCollect = false;
  69.             lock (internalSyncObject) {
  70.                 if (suspendCount > 0) {
  71.                     suspendCount--;
  72.                 }
  73.                
  74.                 if (suspendCount == 0) {
  75.                     for (int i = 0; i < handleTypeCount; i++) {
  76.                         lock (handleTypes[i]) {
  77.                             if (handleTypes[i].NeedCollection()) {
  78.                                 performCollect = true;
  79.                             }
  80.                         }
  81.                     }
  82.                 }
  83.             }
  84.            
  85.             if (performCollect) {
  86.                 GC.Collect();
  87.             }
  88.         }
  89.        
  90.         /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.RegisterType"]/*' />
  91.         /// <devdoc>
  92.         /// Registers a new type of handle with the handle collector.
  93.         /// </devdoc>
  94.         static internal int RegisterType(string typeName, int expense, int initialThreshold)
  95.         {
  96.             lock (internalSyncObject) {
  97.                 if (handleTypeCount == 0 || handleTypeCount == handleTypes.Length) {
  98.                     HandleType[] newTypes = new HandleType[handleTypeCount + 10];
  99.                     if (handleTypes != null) {
  100.                         Array.Copy(handleTypes, 0, newTypes, 0, handleTypeCount);
  101.                     }
  102.                     handleTypes = newTypes;
  103.                 }
  104.                
  105.                 handleTypes[handleTypeCount++] = new HandleType(typeName, expense, initialThreshold);
  106.                 return handleTypeCount;
  107.             }
  108.         }
  109.        
  110.         /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.Remove"]/*' />
  111.         /// <devdoc>
  112.         /// Removes the given handle from the handle collector. Removing a
  113.         /// handle removes it from our "hot list" of objects that should be
  114.         /// frequently garbage collected.
  115.         /// </devdoc>
  116.         static internal IntPtr Remove(IntPtr handle, int type)
  117.         {
  118.             return handleTypes[type - 1].Remove(handle);
  119.         }
  120.        
  121.         /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.HandleType"]/*' />
  122.         /// <devdoc>
  123.         /// Represents a specific type of handle.
  124.         /// </devdoc>
  125.         private class HandleType
  126.         {
  127.             internal readonly string name;
  128.            
  129.             private int initialThreshHold;
  130.             private int threshHold;
  131.             private int handleCount;
  132.             private readonly int deltaPercent;
  133.            
  134.             #if DEBUG_HANDLECOLLECTOR
  135.             private List<IntPtr> handles = new List<IntPtr>();
  136.             #endif
  137.            
  138.             /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.HandleType.HandleType"]/*' />
  139.             /// <devdoc>
  140.             /// Creates a new handle type.
  141.             /// </devdoc>
  142.             internal HandleType(string name, int expense, int initialThreshHold)
  143.             {
  144.                 this.name = name;
  145.                 this.initialThreshHold = initialThreshHold;
  146.                 this.threshHold = initialThreshHold;
  147.                 this.deltaPercent = 100 - expense;
  148.             }
  149.            
  150.             /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.HandleType.Add"]/*' />
  151.             /// <devdoc>
  152.             /// Adds a handle to this handle type for monitoring.
  153.             /// </devdoc>
  154.             [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
  155.             internal void Add(IntPtr handle)
  156.             {
  157.                 if (handle == IntPtr.Zero) {
  158.                     return;
  159.                 }
  160.                
  161.                 bool performCollect = false;
  162.                 int currentCount = 0;
  163.                
  164.                 lock (this) {
  165.                     handleCount++;
  166.                     #if DEBUG_HANDLECOLLECTOR
  167.                     Debug.Assert(!handles.Contains(handle));
  168.                     handles.Add(handle);
  169.                     #endif
  170.                     performCollect = NeedCollection();
  171.                     currentCount = handleCount;
  172.                 }
  173.                 lock (internalSyncObject) {
  174.                     if (HandleCollector.HandleAdded != null) {
  175.                         HandleCollector.HandleAdded(name, handle, currentCount);
  176.                     }
  177.                 }
  178.                
  179.                 if (!performCollect) {
  180.                     return;
  181.                 }
  182.                
  183.                
  184.                 if (performCollect) {
  185.                     #if DEBUG_HANDLECOLLECTOR
  186.                     Debug.WriteLine("HC> Forcing garbage collect");
  187.                     Debug.WriteLine("HC> name :" + name);
  188.                     Debug.WriteLine("HC> threshHold :" + (threshHold).ToString());
  189.                     Debug.WriteLine("HC> handleCount :" + (handleCount).ToString());
  190.                     Debug.WriteLine("HC> deltaPercent:" + (deltaPercent).ToString());
  191.                     #endif
  192.                     GC.Collect();
  193.                    
  194.                     // We just performed a GC. If the main thread is in a tight
  195.                     // loop there is a this will cause us to increase handles forever and prevent handle collector
  196.                     // from doing its job. Yield the thread here. This won't totally cause
  197.                     // a finalization pass but it will effectively elevate the priority
  198.                     // of the finalizer thread just for an instant. But how long should
  199.                     // we sleep? We base it on how expensive the handles are because the
  200.                     // more expensive the handle, the more critical that it be reclaimed.
  201.                     int sleep = (100 - deltaPercent) / 4;
  202.                     System.Threading.Thread.Sleep(sleep);
  203.                 }
  204.             }
  205.            
  206.            
  207.             /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.HandleType.GetHandleCount"]/*' />
  208.             /// <devdoc>
  209.             /// Retrieves the outstanding handle count for this
  210.             /// handle type.
  211.             /// </devdoc>
  212.             [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  213.             internal int GetHandleCount()
  214.             {
  215.                 lock (this) {
  216.                     return handleCount;
  217.                 }
  218.             }
  219.            
  220.             /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.HandleType.NeedCollection"]/*' />
  221.             /// <devdoc>
  222.             /// Determines if this handle type needs a garbage collection pass.
  223.             /// </devdoc>
  224.             internal bool NeedCollection()
  225.             {
  226.                
  227.                 if (suspendCount > 0) {
  228.                     return false;
  229.                 }
  230.                 if (handleCount > threshHold) {
  231.                     threshHold = handleCount + ((handleCount * deltaPercent) / 100);
  232.                     #if DEBUG_HANDLECOLLECTOR
  233.                     Debug.WriteLine("HC> NeedCollection: increase threshHold to " + threshHold);
  234.                     #endif
  235.                     return true;
  236.                 }
  237.                
  238.                 // If handle count < threshHold, we don't
  239.                 // need to collect, but if it 10% below the next lowest threshhold we
  240.                 // will bump down a rung. We need to choose a percentage here or else
  241.                 // we will oscillate.
  242.                 //
  243.                 int oldThreshHold = (100 * threshHold) / (100 + deltaPercent);
  244.                 if (oldThreshHold >= initialThreshHold && handleCount < (int)(oldThreshHold * 0.9f)) {
  245.                     #if DEBUG_HANDLECOLLECTOR
  246.                     Debug.WriteLine("HC> NeedCollection: throttle threshhold " + threshHold + " down to " + oldThreshHold);
  247.                     #endif
  248.                     threshHold = oldThreshHold;
  249.                 }
  250.                
  251.                 return false;
  252.             }
  253.            
  254.             /// <include file='doc\NativeMethods.uex' path='docs/doc[@for="NativeMethods.HandleCollector.HandleType.Remove"]/*' />
  255.             /// <devdoc>
  256.             /// Removes the given handle from our monitor list.
  257.             /// </devdoc>
  258.             internal IntPtr Remove(IntPtr handle)
  259.             {
  260.                 if (handle == IntPtr.Zero) {
  261.                     return handle;
  262.                 }
  263.                 int currentCount = 0;
  264.                 lock (this) {
  265.                     handleCount--;
  266.                     #if DEBUG_HANDLECOLLECTOR
  267.                     Debug.Assert(handles.Contains(handle));
  268.                     handles.Remove(handle);
  269.                     #endif
  270.                     if (handleCount < 0) {
  271.                         System.Diagnostics.Debug.Fail("Handle collector underflow for type '" + name + "'");
  272.                         handleCount = 0;
  273.                     }
  274.                     currentCount = handleCount;
  275.                 }
  276.                 lock (internalSyncObject) {
  277.                     if (HandleCollector.HandleRemoved != null) {
  278.                         HandleCollector.HandleRemoved(name, handle, currentCount);
  279.                     }
  280.                 }
  281.                 return handle;
  282.                
  283.             }
  284.         }
  285.     }
  286.    
  287.     internal delegate void HandleChangeEventHandler(string handleType, IntPtr handleValue, int currentHandleCount);
  288. }

Developer Fusion