Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/rx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Rx.NET/System.Reactive.Core/Reactive/Disposables')
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/AnonymousDisposable.cs47
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/BooleanDisposable.cs45
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/CancellationDisposable.cs61
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/CompositeDisposable.cs276
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/ContextDisposable.cs66
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/DefaultDisposable.cs27
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/Disposable.cs32
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/MultipleAssignmentDisposable.cs92
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/RefCountDisposable.cs131
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/ScheduledDisposable.cs85
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/SerialDisposable.cs87
-rw-r--r--Rx.NET/System.Reactive.Core/Reactive/Disposables/SingleAssignmentDisposable.cs80
12 files changed, 1029 insertions, 0 deletions
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/AnonymousDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/AnonymousDisposable.cs
new file mode 100644
index 0000000..f31bbf4
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/AnonymousDisposable.cs
@@ -0,0 +1,47 @@
+// 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
+{
+ /// <summary>
+ /// Represents an Action-based disposable.
+ /// </summary>
+ internal sealed class AnonymousDisposable : ICancelable
+ {
+ private volatile Action _dispose;
+
+ /// <summary>
+ /// Constructs a new disposable with the given action used for disposal.
+ /// </summary>
+ /// <param name="dispose">Disposal action which will be run upon calling Dispose.</param>
+ public AnonymousDisposable(Action dispose)
+ {
+ System.Diagnostics.Debug.Assert(dispose != null);
+
+ _dispose = dispose;
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get { return _dispose == null; }
+ }
+
+ /// <summary>
+ /// Calls the disposal action if and only if the current instance hasn't been disposed yet.
+ /// </summary>
+ public void Dispose()
+ {
+#pragma warning disable 0420
+ var dispose = Interlocked.Exchange(ref _dispose, null);
+#pragma warning restore 0420
+ if (dispose != null)
+ {
+ dispose();
+ }
+ }
+ }
+}
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/BooleanDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/BooleanDisposable.cs
new file mode 100644
index 0000000..1f1a21f
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/BooleanDisposable.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace System.Reactive.Disposables
+{
+ /// <summary>
+ /// Represents a disposable resource that can be checked for disposal status.
+ /// </summary>
+ public sealed class BooleanDisposable : ICancelable
+ {
+ // Keep internal! This is used as sentinel in other IDisposable implementations to detect disposal and
+ // should never be exposed to user code in order to prevent users from swapping in the sentinel. Have
+ // a look at the code in e.g. SingleAssignmentDisposable for usage patterns.
+ internal static readonly BooleanDisposable True = new BooleanDisposable(true);
+
+ private volatile bool _isDisposed;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.BooleanDisposable"/> class.
+ /// </summary>
+ public BooleanDisposable()
+ {
+ }
+
+ private BooleanDisposable(bool isDisposed)
+ {
+ _isDisposed = isDisposed;
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get { return _isDisposed; }
+ }
+
+ /// <summary>
+ /// Sets the status to disposed, which can be observer through the <see cref="IsDisposed"/> property.
+ /// </summary>
+ public void Dispose()
+ {
+ _isDisposed = true;
+ }
+ }
+}
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/CancellationDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/CancellationDisposable.cs
new file mode 100644
index 0000000..5c67511
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/CancellationDisposable.cs
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+#if !NO_TPL
+using System.Threading;
+
+namespace System.Reactive.Disposables
+{
+ /// <summary>
+ /// Represents a disposable resource that has an associated <seealso cref="T:System.Threading.CancellationToken"/> that will be set to the cancellation requested state upon disposal.
+ /// </summary>
+ public sealed class CancellationDisposable : ICancelable
+ {
+ private readonly CancellationTokenSource _cts;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CancellationDisposable"/> class that uses an existing <seealso cref="T:System.Threading.CancellationTokenSource"/>.
+ /// </summary>
+ /// <param name="cts"><seealso cref="T:System.Threading.CancellationTokenSource"/> used for cancellation.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="cts"/> is null.</exception>
+ public CancellationDisposable(CancellationTokenSource cts)
+ {
+ if (cts == null)
+ throw new ArgumentNullException("cts");
+
+ _cts = cts;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CancellationDisposable"/> class that uses a new <seealso cref="T:System.Threading.CancellationTokenSource"/>.
+ /// </summary>
+ public CancellationDisposable()
+ : this(new CancellationTokenSource())
+ {
+ }
+
+ /// <summary>
+ /// Gets the <see cref="T:System.Threading.CancellationToken"/> used by this CancellationDisposable.
+ /// </summary>
+ public CancellationToken Token
+ {
+ get { return _cts.Token; }
+ }
+
+ /// <summary>
+ /// Cancels the underlying <seealso cref="T:System.Threading.CancellationTokenSource"/>.
+ /// </summary>
+ public void Dispose()
+ {
+ _cts.Cancel();
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get { return _cts.IsCancellationRequested; }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/CompositeDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/CompositeDisposable.cs
new file mode 100644
index 0000000..6416cff
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/CompositeDisposable.cs
@@ -0,0 +1,276 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace System.Reactive.Disposables
+{
+ /// <summary>
+ /// Represents a group of disposable resources that are disposed together.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "Backward compat + ideally want to get rid of the ICollection nature of the type.")]
+ public sealed class CompositeDisposable : ICollection<IDisposable>, ICancelable
+ {
+ private readonly object _gate = new object();
+
+ private bool _disposed;
+ private List<IDisposable> _disposables;
+ private int _count;
+ private const int SHRINK_THRESHOLD = 64;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CompositeDisposable"/> class with no disposables contained by it initially.
+ /// </summary>
+ public CompositeDisposable()
+ {
+ _disposables = new List<IDisposable>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CompositeDisposable"/> class with the specified number of disposables.
+ /// </summary>
+ /// <param name="capacity">The number of disposables that the new CompositeDisposable can initially store.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="capacity"/> is less than zero.</exception>
+ public CompositeDisposable(int capacity)
+ {
+ if (capacity < 0)
+ throw new ArgumentOutOfRangeException("capacity");
+
+ _disposables = new List<IDisposable>(capacity);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CompositeDisposable"/> class from a group of disposables.
+ /// </summary>
+ /// <param name="disposables">Disposables that will be disposed together.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="disposables"/> is null.</exception>
+ public CompositeDisposable(params IDisposable[] disposables)
+ {
+ if (disposables == null)
+ throw new ArgumentNullException("disposables");
+
+ _disposables = new List<IDisposable>(disposables);
+ _count = _disposables.Count;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CompositeDisposable"/> class from a group of disposables.
+ /// </summary>
+ /// <param name="disposables">Disposables that will be disposed together.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="disposables"/> is null.</exception>
+ public CompositeDisposable(IEnumerable<IDisposable> disposables)
+ {
+ if (disposables == null)
+ throw new ArgumentNullException("disposables");
+
+ _disposables = new List<IDisposable>(disposables);
+ _count = _disposables.Count;
+ }
+
+ /// <summary>
+ /// Gets the number of disposables contained in the CompositeDisposable.
+ /// </summary>
+ public int Count
+ {
+ get
+ {
+ return _count;
+ }
+ }
+
+ /// <summary>
+ /// Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed.
+ /// </summary>
+ /// <param name="item">Disposable to add.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="item"/> is null.</exception>
+ public void Add(IDisposable item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ var shouldDispose = false;
+ lock (_gate)
+ {
+ shouldDispose = _disposed;
+ if (!_disposed)
+ {
+ _disposables.Add(item);
+ _count++;
+ }
+ }
+ if (shouldDispose)
+ item.Dispose();
+ }
+
+ /// <summary>
+ /// Removes and disposes the first occurrence of a disposable from the CompositeDisposable.
+ /// </summary>
+ /// <param name="item">Disposable to remove.</param>
+ /// <returns>true if found; false otherwise.</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="item"/> is null.</exception>
+ public bool Remove(IDisposable item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ var shouldDispose = false;
+
+ lock (_gate)
+ {
+ if (!_disposed)
+ {
+ //
+ // List<T> doesn't shrink the size of the underlying array but does collapse the array
+ // by copying the tail one position to the left of the removal index. We don't need
+ // index-based lookup but only ordering for sequential disposal. So, instead of spending
+ // cycles on the Array.Copy imposed by Remove, we use a null sentinel value. We also
+ // do manual Swiss cheese detection to shrink the list if there's a lot of holes in it.
+ //
+ var i = _disposables.IndexOf(item);
+ if (i >= 0)
+ {
+ shouldDispose = true;
+ _disposables[i] = null;
+ _count--;
+
+ if (_disposables.Capacity > SHRINK_THRESHOLD && _count < _disposables.Capacity / 2)
+ {
+ var old = _disposables;
+ _disposables = new List<IDisposable>(_disposables.Capacity / 2);
+
+ foreach (var d in old)
+ if (d != null)
+ _disposables.Add(d);
+ }
+ }
+ }
+ }
+
+ if (shouldDispose)
+ item.Dispose();
+
+ return shouldDispose;
+ }
+
+ /// <summary>
+ /// Disposes all disposables in the group and removes them from the group.
+ /// </summary>
+ public void Dispose()
+ {
+ var currentDisposables = default(IDisposable[]);
+ lock (_gate)
+ {
+ if (!_disposed)
+ {
+ _disposed = true;
+ currentDisposables = _disposables.ToArray();
+ _disposables.Clear();
+ _count = 0;
+ }
+ }
+
+ if (currentDisposables != null)
+ {
+ foreach (var d in currentDisposables)
+ if (d != null)
+ d.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Removes and disposes all disposables from the CompositeDisposable, but does not dispose the CompositeDisposable.
+ /// </summary>
+ public void Clear()
+ {
+ var currentDisposables = default(IDisposable[]);
+ lock (_gate)
+ {
+ currentDisposables = _disposables.ToArray();
+ _disposables.Clear();
+ _count = 0;
+ }
+
+ foreach (var d in currentDisposables)
+ if (d != null)
+ d.Dispose();
+ }
+
+ /// <summary>
+ /// Determines whether the CompositeDisposable contains a specific disposable.
+ /// </summary>
+ /// <param name="item">Disposable to search for.</param>
+ /// <returns>true if the disposable was found; otherwise, false.</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="item"/> is null.</exception>
+ public bool Contains(IDisposable item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ lock (_gate)
+ {
+ return _disposables.Contains(item);
+ }
+ }
+
+ /// <summary>
+ /// Copies the disposables contained in the CompositeDisposable to an array, starting at a particular array index.
+ /// </summary>
+ /// <param name="array">Array to copy the contained disposables to.</param>
+ /// <param name="arrayIndex">Target index at which to copy the first disposable of the group.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="array"/> is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than zero. -or - <paramref name="arrayIndex"/> is larger than or equal to the array length.</exception>
+ public void CopyTo(IDisposable[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ if (arrayIndex < 0 || arrayIndex >= array.Length)
+ throw new ArgumentOutOfRangeException("arrayIndex");
+
+ lock (_gate)
+ {
+ Array.Copy(_disposables.Where(d => d != null).ToArray(), 0, array, arrayIndex, array.Length - arrayIndex);
+ }
+ }
+
+ /// <summary>
+ /// Always returns false.
+ /// </summary>
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the CompositeDisposable.
+ /// </summary>
+ /// <returns>An enumerator to iterate over the disposables.</returns>
+ public IEnumerator<IDisposable> GetEnumerator()
+ {
+ var res = default(IEnumerable<IDisposable>);
+
+ lock (_gate)
+ {
+ res = _disposables.Where(d => d != null).ToList();
+ }
+
+ return res.GetEnumerator();
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the CompositeDisposable.
+ /// </summary>
+ /// <returns>An enumerator to iterate over the disposables.</returns>
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get { return _disposed; }
+ }
+ }
+}
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/ContextDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/ContextDisposable.cs
new file mode 100644
index 0000000..ebe3479
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/ContextDisposable.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+#if !NO_SYNCCTX
+using System.Reactive.Concurrency;
+using System.Threading;
+
+namespace System.Reactive.Disposables
+{
+ /// <summary>
+ /// Represents a disposable resource whose disposal invocation will be posted to the specified <seealso cref="T:System.Threading.SynchronizationContext"/>.
+ /// </summary>
+ public sealed class ContextDisposable : ICancelable
+ {
+ private readonly SynchronizationContext _context;
+ private volatile IDisposable _disposable;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.ContextDisposable"/> class that uses the specified <see cref="T:System.Threading.SynchronizationContext"/> on which to dispose the specified disposable resource.
+ /// </summary>
+ /// <param name="context">Context to perform disposal on.</param>
+ /// <param name="disposable">Disposable whose Dispose operation to run on the given synchronization context.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="context"/> or <paramref name="disposable"/> is null.</exception>
+ public ContextDisposable(SynchronizationContext context, IDisposable disposable)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+ if (disposable == null)
+ throw new ArgumentNullException("disposable");
+
+ _context = context;
+ _disposable = disposable;
+ }
+
+ /// <summary>
+ /// Gets the provided <see cref="T:System.Threading.SynchronizationContext"/>.
+ /// </summary>
+ public SynchronizationContext Context
+ {
+ get { return _context; }
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get { return _disposable == BooleanDisposable.True; }
+ }
+
+ /// <summary>
+ /// Disposes the underlying disposable on the provided <see cref="T:System.Threading.SynchronizationContext"/>.
+ /// </summary>
+ public void Dispose()
+ {
+#pragma warning disable 0420
+ var disposable = Interlocked.Exchange(ref _disposable, BooleanDisposable.True);
+#pragma warning restore 0420
+
+ if (disposable != BooleanDisposable.True)
+ {
+ _context.PostWithStartComplete(d => d.Dispose(), disposable);
+ }
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/DefaultDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/DefaultDisposable.cs
new file mode 100644
index 0000000..6f643c3
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/DefaultDisposable.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace System.Reactive.Disposables
+{
+ /// <summary>
+ /// Represents a disposable that does nothing on disposal.
+ /// </summary>
+ internal sealed class DefaultDisposable : IDisposable
+ {
+ /// <summary>
+ /// Singleton default disposable.
+ /// </summary>
+ public static readonly DefaultDisposable Instance = new DefaultDisposable();
+
+ private DefaultDisposable()
+ {
+ }
+
+ /// <summary>
+ /// Does nothing.
+ /// </summary>
+ public void Dispose()
+ {
+ // no op
+ }
+ }
+}
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/Disposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/Disposable.cs
new file mode 100644
index 0000000..fc77f3b
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/Disposable.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace System.Reactive.Disposables
+{
+ /// <summary>
+ /// Provides a set of static methods for creating Disposables.
+ /// </summary>
+ public static class Disposable
+ {
+ /// <summary>
+ /// Gets the disposable that does nothing when disposed.
+ /// </summary>
+ public static IDisposable Empty
+ {
+ get { return DefaultDisposable.Instance; }
+ }
+
+ /// <summary>
+ /// Creates a disposable object that invokes the specified action when disposed.
+ /// </summary>
+ /// <param name="dispose">Action to run during the first call to <see cref="IDisposable.Dispose"/>. The action is guaranteed to be run at most once.</param>
+ /// <returns>The disposable object that runs the given action upon disposal.</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="dispose"/> is null.</exception>
+ public static IDisposable Create(Action dispose)
+ {
+ if (dispose == null)
+ throw new ArgumentNullException("dispose");
+
+ return new AnonymousDisposable(dispose);
+ }
+ }
+}
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/MultipleAssignmentDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/MultipleAssignmentDisposable.cs
new file mode 100644
index 0000000..0fcc4dc
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/MultipleAssignmentDisposable.cs
@@ -0,0 +1,92 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace System.Reactive.Disposables
+{
+ /// <summary>
+ /// Represents a disposable resource whose underlying disposable resource can be swapped for another disposable resource.
+ /// </summary>
+ public sealed class MultipleAssignmentDisposable : ICancelable
+ {
+ private readonly object _gate = new object();
+ private IDisposable _current;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.MultipleAssignmentDisposable"/> class with no current underlying disposable.
+ /// </summary>
+ public MultipleAssignmentDisposable()
+ {
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get
+ {
+ lock (_gate)
+ {
+ // We use a sentinel value to indicate we've been disposed. This sentinel never leaks
+ // to the outside world (see the Disposable property getter), so no-one can ever assign
+ // this value to us manually.
+ return _current == BooleanDisposable.True;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
+ /// </summary>
+ /// <remarks>If the MutableDisposable has already been disposed, assignment to this property causes immediate disposal of the given disposable object.</remarks>
+ public IDisposable Disposable
+ {
+ get
+ {
+ lock (_gate)
+ {
+ if (_current == BooleanDisposable.True)
+ return DefaultDisposable.Instance; // Don't leak the sentinel value.
+
+ return _current;
+ }
+ }
+
+ set
+ {
+ var shouldDispose = false;
+ lock (_gate)
+ {
+ shouldDispose = IsDisposed;
+ if (!shouldDispose)
+ {
+ _current = value;
+ }
+ }
+ if (shouldDispose && value != null)
+ value.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Disposes the underlying disposable as well as all future replacements.
+ /// </summary>
+ public void Dispose()
+ {
+ var old = default(IDisposable);
+
+ lock (_gate)
+ {
+ if (!IsDisposed)
+ {
+ old = _current;
+
+ // See IsDisposed for rationale behind using the sentinel.
+ _current = BooleanDisposable.True;
+ }
+ }
+
+ if (old != null)
+ old.Dispose();
+ }
+ }
+}
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/RefCountDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/RefCountDisposable.cs
new file mode 100644
index 0000000..9bd0407
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/RefCountDisposable.cs
@@ -0,0 +1,131 @@
+// 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
+{
+ /// <summary>
+ /// Represents a disposable resource that only disposes its underlying disposable resource when all <see cref="GetDisposable">dependent disposable objects</see> have been disposed.
+ /// </summary>
+ public sealed class RefCountDisposable : ICancelable
+ {
+ private readonly object _gate = new object();
+ private IDisposable _disposable;
+ private bool _isPrimaryDisposed;
+ private int _count;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.RefCountDisposable"/> class with the specified disposable.
+ /// </summary>
+ /// <param name="disposable">Underlying disposable.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="disposable"/> is null.</exception>
+ public RefCountDisposable(IDisposable disposable)
+ {
+ if (disposable == null)
+ throw new ArgumentNullException("disposable");
+
+ _disposable = disposable;
+ _isPrimaryDisposed = false;
+ _count = 0;
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get { return _disposable == null; }
+ }
+
+ /// <summary>
+ /// Returns a dependent disposable that when disposed decreases the refcount on the underlying disposable.
+ /// </summary>
+ /// <returns>A dependent disposable contributing to the reference count that manages the underlying disposable's lifetime.</returns>
+ [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);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Disposes the underlying disposable only when all dependent disposables have been disposed.
+ /// </summary>
+ 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();
+ }
+ }
+ }
+}
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/ScheduledDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/ScheduledDisposable.cs
new file mode 100644
index 0000000..eb742aa
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/ScheduledDisposable.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System.Reactive.Concurrency;
+using System.Threading;
+
+namespace System.Reactive.Disposables
+{
+ /// <summary>
+ /// Represents a disposable resource whose disposal invocation will be scheduled on the specified <seealso cref="T:System.Reactive.Concurrency.IScheduler"/>.
+ /// </summary>
+ public sealed class ScheduledDisposable : ICancelable
+ {
+ private readonly IScheduler _scheduler;
+ private volatile IDisposable _disposable;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.ScheduledDisposable"/> class that uses an <see cref="T:System.Reactive.Concurrency.IScheduler"/> on which to dispose the disposable.
+ /// </summary>
+ /// <param name="scheduler">Scheduler where the disposable resource will be disposed on.</param>
+ /// <param name="disposable">Disposable resource to dispose on the given scheduler.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="disposable"/> is null.</exception>
+ public ScheduledDisposable(IScheduler scheduler, IDisposable disposable)
+ {
+ if (scheduler == null)
+ throw new ArgumentNullException("scheduler");
+ if (disposable == null)
+ throw new ArgumentNullException("disposable");
+
+ _scheduler = scheduler;
+ _disposable = disposable;
+ }
+
+ /// <summary>
+ /// Gets the scheduler where the disposable resource will be disposed on.
+ /// </summary>
+ public IScheduler Scheduler
+ {
+ get { return _scheduler; }
+ }
+
+ /// <summary>
+ /// Gets the underlying disposable. After disposal, the result is undefined.
+ /// </summary>
+ public IDisposable Disposable
+ {
+ get
+ {
+ var current = _disposable;
+
+ if (current == BooleanDisposable.True)
+ return DefaultDisposable.Instance; // Don't leak the sentinel value.
+
+ return current;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get { return _disposable == BooleanDisposable.True; }
+ }
+
+ /// <summary>
+ /// Disposes the wrapped disposable on the provided scheduler.
+ /// </summary>
+ public void Dispose()
+ {
+ Scheduler.Schedule(DisposeInner);
+ }
+
+ private void DisposeInner()
+ {
+#pragma warning disable 0420
+ var disposable = Interlocked.Exchange(ref _disposable, BooleanDisposable.True);
+#pragma warning restore 0420
+
+ if (disposable != BooleanDisposable.True)
+ {
+ disposable.Dispose();
+ }
+ }
+ }
+}
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/SerialDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/SerialDisposable.cs
new file mode 100644
index 0000000..835bd68
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/SerialDisposable.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+namespace System.Reactive.Disposables
+{
+ /// <summary>
+ /// Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource.
+ /// </summary>
+ public sealed class SerialDisposable : ICancelable
+ {
+ private readonly object _gate = new object();
+ private IDisposable _current;
+ private bool _disposed;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.SerialDisposable"/> class.
+ /// </summary>
+ public SerialDisposable()
+ {
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get
+ {
+ lock (_gate)
+ {
+ return _disposed;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the underlying disposable.
+ /// </summary>
+ /// <remarks>If the SerialDisposable has already been disposed, assignment to this property causes immediate disposal of the given disposable object. Assigning this property disposes the previous disposable object.</remarks>
+ public IDisposable Disposable
+ {
+ get
+ {
+ return _current;
+ }
+
+ set
+ {
+ var shouldDispose = false;
+ var old = default(IDisposable);
+ lock (_gate)
+ {
+ shouldDispose = _disposed;
+ if (!shouldDispose)
+ {
+ old = _current;
+ _current = value;
+ }
+ }
+ if (old != null)
+ old.Dispose();
+ if (shouldDispose && value != null)
+ value.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Disposes the underlying disposable as well as all future replacements.
+ /// </summary>
+ public void Dispose()
+ {
+ var old = default(IDisposable);
+
+ lock (_gate)
+ {
+ if (!_disposed)
+ {
+ _disposed = true;
+ old = _current;
+ _current = null;
+ }
+ }
+
+ if (old != null)
+ old.Dispose();
+ }
+ }
+}
diff --git a/Rx.NET/System.Reactive.Core/Reactive/Disposables/SingleAssignmentDisposable.cs b/Rx.NET/System.Reactive.Core/Reactive/Disposables/SingleAssignmentDisposable.cs
new file mode 100644
index 0000000..8931c25
--- /dev/null
+++ b/Rx.NET/System.Reactive.Core/Reactive/Disposables/SingleAssignmentDisposable.cs
@@ -0,0 +1,80 @@
+// 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
+{
+ /// <summary>
+ /// Represents a disposable resource which only allows a single assignment of its underlying disposable resource.
+ /// If an underlying disposable resource has already been set, future attempts to set the underlying disposable resource will throw an <see cref="T:System.InvalidOperationException"/>.
+ /// </summary>
+ public sealed class SingleAssignmentDisposable : ICancelable
+ {
+ private volatile IDisposable _current;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.SingleAssignmentDisposable"/> class.
+ /// </summary>
+ public SingleAssignmentDisposable()
+ {
+ }
+
+ /// <summary>
+ /// Gets a value that indicates whether the object is disposed.
+ /// </summary>
+ public bool IsDisposed
+ {
+ get
+ {
+ // We use a sentinel value to indicate we've been disposed. This sentinel never leaks
+ // to the outside world (see the Disposable property getter), so no-one can ever assign
+ // this value to us manually.
+ return _current == BooleanDisposable.True;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown if the SingleAssignmentDisposable has already been assigned to.</exception>
+ public IDisposable Disposable
+ {
+ get
+ {
+ var current = _current;
+
+ if (current == BooleanDisposable.True)
+ return DefaultDisposable.Instance; // Don't leak the sentinel value.
+
+ return current;
+ }
+
+ set
+ {
+#pragma warning disable 0420
+ var old = Interlocked.CompareExchange(ref _current, value, null);
+#pragma warning restore 0420
+ if (old == null)
+ return;
+
+ if (old != BooleanDisposable.True)
+ throw new InvalidOperationException(Strings_Core.DISPOSABLE_ALREADY_ASSIGNED);
+
+ if (value != null)
+ value.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Disposes the underlying disposable.
+ /// </summary>
+ public void Dispose()
+ {
+#pragma warning disable 0420
+ var old = Interlocked.Exchange(ref _current, BooleanDisposable.True);
+#pragma warning restore 0420
+ if (old != null)
+ old.Dispose();
+ }
+ }
+}