DeferredDisposableLifetime.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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.Diagnostics;
  5. namespace System.Threading
  6. {
  7. /// <summary>
  8. /// Provides callbacks to objects whose lifetime is managed by <see cref="DeferredDisposableLifetime{T}"/>.
  9. /// </summary>
  10. internal interface IDeferredDisposable
  11. {
  12. /// <summary>
  13. /// Called when the object's refcount reaches zero.
  14. /// </summary>
  15. /// <param name="disposed">
  16. /// Indicates whether the object has been disposed.
  17. /// </param>
  18. /// <remarks>
  19. /// If the refcount reaches zero before the object is disposed, this method will be called with
  20. /// <paramref name="disposed"/> set to false. If the object is then disposed, this method will be
  21. /// called again, with <paramref name="disposed"/> set to true. If the refcount reaches zero
  22. /// after the object has already been disposed, this will be called a single time, with
  23. /// <paramref name="disposed"/> set to true.
  24. /// </remarks>
  25. void OnFinalRelease(bool disposed);
  26. }
  27. /// <summary>
  28. /// Manages the lifetime of an object which implements IDisposable, but which must defer the actual
  29. /// cleanup of state until all existing uses of the object are complete.
  30. /// </summary>
  31. /// <typeparam name="T">The type of object whose lifetime will be managed.</typeparam>
  32. /// <remarks>
  33. /// This type maintains a reference count, and tracks whether the object has been disposed. When
  34. /// Callbacks are made to <see cref="IDeferredDisposable.OnFinalRelease(bool)"/> when the refcount
  35. /// reaches zero. Objects that need to defer cleanup until they have been disposed *and* they have
  36. /// no more references can do so in <see cref="IDeferredDisposable.OnFinalRelease(bool)"/> when
  37. /// 'disposed' is true.
  38. /// </remarks>
  39. internal struct DeferredDisposableLifetime<T> where T : class, IDeferredDisposable
  40. {
  41. /// <summary>_count is positive until Dispose is called, after which it's (-1 - refcount).</summary>
  42. private int _count;
  43. public bool AddRef(T obj)
  44. {
  45. while (true)
  46. {
  47. int oldCount = Volatile.Read(ref _count);
  48. // Have we been disposed?
  49. if (oldCount < 0)
  50. throw new ObjectDisposedException(typeof(T).ToString());
  51. int newCount = checked(oldCount + 1);
  52. if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
  53. return true;
  54. }
  55. }
  56. public void Release(T obj)
  57. {
  58. while (true)
  59. {
  60. int oldCount = Volatile.Read(ref _count);
  61. if (oldCount > 0)
  62. {
  63. // We haven't been disposed. Decrement _count.
  64. int newCount = oldCount - 1;
  65. if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
  66. {
  67. if (newCount == 0)
  68. obj.OnFinalRelease(disposed: false);
  69. return;
  70. }
  71. }
  72. else
  73. {
  74. Debug.Assert(oldCount != 0 && oldCount != -1);
  75. // We've been disposed. Increment _count.
  76. int newCount = oldCount + 1;
  77. if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
  78. {
  79. if (newCount == -1)
  80. obj.OnFinalRelease(disposed: true);
  81. return;
  82. }
  83. }
  84. }
  85. }
  86. public void Dispose(T obj)
  87. {
  88. while (true)
  89. {
  90. int oldCount = Volatile.Read(ref _count);
  91. if (oldCount < 0)
  92. return; // already disposed
  93. int newCount = -1 - oldCount;
  94. if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
  95. {
  96. if (newCount == -1)
  97. obj.OnFinalRelease(disposed: true);
  98. return;
  99. }
  100. }
  101. }
  102. }
  103. }