// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System.Threading; namespace System.Reactive.Disposables { /// /// Represents a disposable resource that only disposes its underlying disposable resource when all dependent disposable objects have been disposed. /// public sealed class RefCountDisposable : ICancelable { private readonly object _gate = new object(); private IDisposable _disposable; private bool _isPrimaryDisposed; private int _count; /// /// Initializes a new instance of the class with the specified disposable. /// /// Underlying disposable. /// is null. public RefCountDisposable(IDisposable disposable) { if (disposable == null) throw new ArgumentNullException("disposable"); _disposable = disposable; _isPrimaryDisposed = false; _count = 0; } /// /// Gets a value that indicates whether the object is disposed. /// public bool IsDisposed { get { return _disposable == null; } } /// /// Returns a dependent disposable that when disposed decreases the refcount on the underlying disposable. /// /// A dependent disposable contributing to the reference count that manages the underlying disposable's lifetime. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Backward compat + non-trivial work for a property getter.")] public IDisposable GetDisposable() { lock (_gate) { if (_disposable == null) { return Disposable.Empty; } else { _count++; return new InnerDisposable(this); } } } /// /// Disposes the underlying disposable only when all dependent disposables have been disposed. /// public void Dispose() { var disposable = default(IDisposable); lock (_gate) { if (_disposable != null) { if (!_isPrimaryDisposed) { _isPrimaryDisposed = true; if (_count == 0) { disposable = _disposable; _disposable = null; } } } } if (disposable != null) disposable.Dispose(); } private void Release() { var disposable = default(IDisposable); lock (_gate) { if (_disposable != null) { _count--; System.Diagnostics.Debug.Assert(_count >= 0); if (_isPrimaryDisposed) { if (_count == 0) { disposable = _disposable; _disposable = null; } } } } if (disposable != null) disposable.Dispose(); } sealed class InnerDisposable : IDisposable { private RefCountDisposable _parent; public InnerDisposable(RefCountDisposable parent) { _parent = parent; } public void Dispose() { var parent = Interlocked.Exchange(ref _parent, null); if (parent != null) parent.Release(); } } } }