RefCountDisposable
using System.Threading;
namespace System.Reactive.Disposables
{
public sealed class RefCountDisposable : ICancelable, IDisposable
{
private sealed class InnerDisposable : IDisposable
{
private RefCountDisposable _parent;
public InnerDisposable(RefCountDisposable parent)
{
_parent = parent;
}
public void Dispose()
{
Interlocked.Exchange(ref _parent, null)?.Release();
}
}
private readonly bool _throwWhenDisposed;
private readonly object _gate = new object();
private IDisposable _disposable;
private bool _isPrimaryDisposed;
private int _count;
public bool IsDisposed => _disposable == null;
public RefCountDisposable(IDisposable disposable)
: this(disposable, false)
{
}
public RefCountDisposable(IDisposable disposable, bool throwWhenDisposed)
{
if (disposable == null)
throw new ArgumentNullException("disposable");
_disposable = disposable;
_isPrimaryDisposed = false;
_count = 0;
_throwWhenDisposed = throwWhenDisposed;
}
public IDisposable GetDisposable()
{
lock (_gate) {
if (_disposable != null) {
_count++;
return new InnerDisposable(this);
}
if (_throwWhenDisposed)
throw new ObjectDisposedException("RefCountDisposable");
return Disposable.Empty;
}
}
public void Dispose()
{
IDisposable disposable = null;
lock (_gate) {
if (_disposable != null && !_isPrimaryDisposed) {
_isPrimaryDisposed = true;
if (_count == 0) {
disposable = _disposable;
_disposable = null;
}
}
}
disposable?.Dispose();
}
private void Release()
{
IDisposable disposable = null;
lock (_gate) {
if (_disposable != null) {
_count--;
if (_isPrimaryDisposed && _count == 0) {
disposable = _disposable;
_disposable = null;
}
}
}
disposable?.Dispose();
}
}
}