// 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 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 . /// public sealed class SingleAssignmentDisposable : ICancelable { private volatile IDisposable _current; /// /// Initializes a new instance of the class. /// public SingleAssignmentDisposable() { } /// /// Gets a value that indicates whether the object is disposed. /// 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; } } /// /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined. /// /// Thrown if the SingleAssignmentDisposable has already been assigned to. 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(); } } /// /// Disposes the underlying disposable. /// public void Dispose() { #pragma warning disable 0420 var old = Interlocked.Exchange(ref _current, BooleanDisposable.True); #pragma warning restore 0420 if (old != null) old.Dispose(); } } }