RefCountDisposable.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System.Threading;
  3. namespace System.Reactive.Disposables
  4. {
  5. /// <summary>
  6. /// Represents a disposable resource that only disposes its underlying disposable resource when all <see cref="GetDisposable">dependent disposable objects</see> have been disposed.
  7. /// </summary>
  8. public sealed class RefCountDisposable : ICancelable
  9. {
  10. private readonly object _gate = new object();
  11. private IDisposable _disposable;
  12. private bool _isPrimaryDisposed;
  13. private int _count;
  14. /// <summary>
  15. /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.RefCountDisposable"/> class with the specified disposable.
  16. /// </summary>
  17. /// <param name="disposable">Underlying disposable.</param>
  18. /// <exception cref="ArgumentNullException"><paramref name="disposable"/> is null.</exception>
  19. public RefCountDisposable(IDisposable disposable)
  20. {
  21. if (disposable == null)
  22. throw new ArgumentNullException("disposable");
  23. _disposable = disposable;
  24. _isPrimaryDisposed = false;
  25. _count = 0;
  26. }
  27. /// <summary>
  28. /// Gets a value that indicates whether the object is disposed.
  29. /// </summary>
  30. public bool IsDisposed
  31. {
  32. get { return _disposable == null; }
  33. }
  34. /// <summary>
  35. /// Returns a dependent disposable that when disposed decreases the refcount on the underlying disposable.
  36. /// </summary>
  37. /// <returns>A dependent disposable contributing to the reference count that manages the underlying disposable's lifetime.</returns>
  38. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Backward compat + non-trivial work for a property getter.")]
  39. public IDisposable GetDisposable()
  40. {
  41. lock (_gate)
  42. {
  43. if (_disposable == null)
  44. {
  45. return Disposable.Empty;
  46. }
  47. else
  48. {
  49. _count++;
  50. return new InnerDisposable(this);
  51. }
  52. }
  53. }
  54. /// <summary>
  55. /// Disposes the underlying disposable only when all dependent disposables have been disposed.
  56. /// </summary>
  57. public void Dispose()
  58. {
  59. var disposable = default(IDisposable);
  60. lock (_gate)
  61. {
  62. if (_disposable != null)
  63. {
  64. if (!_isPrimaryDisposed)
  65. {
  66. _isPrimaryDisposed = true;
  67. if (_count == 0)
  68. {
  69. disposable = _disposable;
  70. _disposable = null;
  71. }
  72. }
  73. }
  74. }
  75. if (disposable != null)
  76. disposable.Dispose();
  77. }
  78. private void Release()
  79. {
  80. var disposable = default(IDisposable);
  81. lock (_gate)
  82. {
  83. if (_disposable != null)
  84. {
  85. _count--;
  86. System.Diagnostics.Debug.Assert(_count >= 0);
  87. if (_isPrimaryDisposed)
  88. {
  89. if (_count == 0)
  90. {
  91. disposable = _disposable;
  92. _disposable = null;
  93. }
  94. }
  95. }
  96. }
  97. if (disposable != null)
  98. disposable.Dispose();
  99. }
  100. sealed class InnerDisposable : IDisposable
  101. {
  102. private RefCountDisposable _parent;
  103. public InnerDisposable(RefCountDisposable parent)
  104. {
  105. _parent = parent;
  106. }
  107. public void Dispose()
  108. {
  109. var parent = Interlocked.Exchange(ref _parent, null);
  110. if (parent != null)
  111. parent.Release();
  112. }
  113. }
  114. }
  115. }