// 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();
}
}
}
}