// This code is borrwed from Rx Official and some modified. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace UniRx { /// /// 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--; if (_isPrimaryDisposed) { if (_count == 0) { disposable = _disposable; _disposable = null; } } } } if (disposable != null) disposable.Dispose(); } sealed class InnerDisposable : IDisposable { private RefCountDisposable _parent; object parentLock = new object(); public InnerDisposable(RefCountDisposable parent) { _parent = parent; } public void Dispose() { RefCountDisposable parent; lock (parentLock) { parent = _parent; _parent = null; } if (parent != null) parent.Release(); } } } public partial class Observable { static IObservable AddRef(IObservable xs, RefCountDisposable r) { return Observable.Create((IObserver observer) => new CompositeDisposable(new IDisposable[] { r.GetDisposable(), xs.Subscribe(observer) })); } } }