Gen2GcCallback.cs 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Runtime.ConstrainedExecution;
  5. using System.Runtime.InteropServices;
  6. namespace System
  7. {
  8. /// <summary>
  9. /// Schedules a callback roughly every gen 2 GC (you may see a Gen 0 an Gen 1 but only once)
  10. /// (We can fix this by capturing the Gen 2 count at startup and testing, but I mostly don't care)
  11. /// </summary>
  12. internal sealed class Gen2GcCallback : CriticalFinalizerObject
  13. {
  14. private Gen2GcCallback()
  15. : base()
  16. {
  17. }
  18. /// <summary>
  19. /// Schedule 'callback' to be called in the next GC. If the callback returns true it is
  20. /// rescheduled for the next Gen 2 GC. Otherwise the callbacks stop.
  21. ///
  22. /// NOTE: This callback will be kept alive until either the callback function returns false,
  23. /// or the target object dies.
  24. /// </summary>
  25. public static void Register(Func<object, bool> callback, object targetObj)
  26. {
  27. // Create a unreachable object that remembers the callback function and target object.
  28. Gen2GcCallback gcCallback = new Gen2GcCallback();
  29. gcCallback.Setup(callback, targetObj);
  30. }
  31. private Func<object, bool> _callback;
  32. private GCHandle _weakTargetObj;
  33. private void Setup(Func<object, bool> callback, object targetObj)
  34. {
  35. _callback = callback;
  36. _weakTargetObj = GCHandle.Alloc(targetObj, GCHandleType.Weak);
  37. }
  38. ~Gen2GcCallback()
  39. {
  40. // Check to see if the target object is still alive.
  41. object targetObj = _weakTargetObj.Target;
  42. if (targetObj == null)
  43. {
  44. // The target object is dead, so this callback object is no longer needed.
  45. _weakTargetObj.Free();
  46. return;
  47. }
  48. // Execute the callback method.
  49. try
  50. {
  51. if (!_callback(targetObj))
  52. {
  53. // If the callback returns false, this callback object is no longer needed.
  54. return;
  55. }
  56. }
  57. catch
  58. {
  59. // Ensure that we still get a chance to resurrect this object, even if the callback throws an exception.
  60. #if DEBUG
  61. // Except in DEBUG, as we really shouldn't be hitting any exceptions here.
  62. throw;
  63. #endif
  64. }
  65. // Resurrect ourselves by re-registering for finalization.
  66. if (!Environment.HasShutdownStarted)
  67. {
  68. GC.ReRegisterForFinalize(this);
  69. }
  70. }
  71. }
  72. }