DeferredDisposableLifetime.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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. //
  42. // _count is positive until Dispose is called, after which it's (-1 - refcount).
  43. //
  44. private int _count;
  45. public bool AddRef(T obj)
  46. {
  47. while (true)
  48. {
  49. int oldCount = Volatile.Read(ref _count);
  50. // Have we been disposed?
  51. if (oldCount < 0)
  52. throw new ObjectDisposedException(typeof(T).ToString());
  53. int newCount = checked(oldCount + 1);
  54. if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
  55. return true;
  56. }
  57. }
  58. public void Release(T obj)
  59. {
  60. while (true)
  61. {
  62. int oldCount = Volatile.Read(ref _count);
  63. if (oldCount > 0)
  64. {
  65. // We haven't been disposed. Decrement _count.
  66. int newCount = oldCount - 1;
  67. if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
  68. {
  69. if (newCount == 0)
  70. obj.OnFinalRelease(disposed: false);
  71. return;
  72. }
  73. }
  74. else
  75. {
  76. Debug.Assert(oldCount != 0 && oldCount != -1);
  77. // We've been disposed. Increment _count.
  78. int newCount = oldCount + 1;
  79. if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
  80. {
  81. if (newCount == -1)
  82. obj.OnFinalRelease(disposed: true);
  83. return;
  84. }
  85. }
  86. }
  87. }
  88. public void Dispose(T obj)
  89. {
  90. while (true)
  91. {
  92. int oldCount = Volatile.Read(ref _count);
  93. if (oldCount < 0)
  94. return; // already disposed
  95. int newCount = -1 - oldCount;
  96. if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
  97. {
  98. if (newCount == -1)
  99. obj.OnFinalRelease(disposed: true);
  100. return;
  101. }
  102. }
  103. }
  104. }
  105. }