|  | @@ -2,6 +2,8 @@
 | 
											
												
													
														|  |  // The .NET Foundation licenses this file to you under the Apache 2.0 License.
 |  |  // The .NET Foundation licenses this file to you under the Apache 2.0 License.
 | 
											
												
													
														|  |  // See the LICENSE file in the project root for more information. 
 |  |  // See the LICENSE file in the project root for more information. 
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +using System.Threading;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  namespace System.Reactive.Disposables
 |  |  namespace System.Reactive.Disposables
 | 
											
												
													
														|  |  {
 |  |  {
 | 
											
												
													
														|  |      /// <summary>
 |  |      /// <summary>
 | 
											
										
											
												
													
														|  | @@ -9,7 +11,6 @@ namespace System.Reactive.Disposables
 | 
											
												
													
														|  |      /// </summary>
 |  |      /// </summary>
 | 
											
												
													
														|  |      public sealed class MultipleAssignmentDisposable : ICancelable
 |  |      public sealed class MultipleAssignmentDisposable : ICancelable
 | 
											
												
													
														|  |      {
 |  |      {
 | 
											
												
													
														|  | -        private readonly object _gate = new object();
 |  | 
 | 
											
												
													
														|  |          private IDisposable _current;
 |  |          private IDisposable _current;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          /// <summary>
 |  |          /// <summary>
 | 
											
										
											
												
													
														|  | @@ -26,13 +27,10 @@ namespace System.Reactive.Disposables
 | 
											
												
													
														|  |          {
 |  |          {
 | 
											
												
													
														|  |              get
 |  |              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;
 |  | 
 | 
											
												
													
														|  | -                }
 |  | 
 | 
											
												
													
														|  | 
 |  | +                // 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 Volatile.Read(ref _current) == BooleanDisposable.True;
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -44,28 +42,37 @@ namespace System.Reactive.Disposables
 | 
											
												
													
														|  |          {
 |  |          {
 | 
											
												
													
														|  |              get
 |  |              get
 | 
											
												
													
														|  |              {
 |  |              {
 | 
											
												
													
														|  | -                lock (_gate)
 |  | 
 | 
											
												
													
														|  | 
 |  | +                var a = Volatile.Read(ref _current);
 | 
											
												
													
														|  | 
 |  | +                // Don't leak the DISPOSED sentinel
 | 
											
												
													
														|  | 
 |  | +                if (a == BooleanDisposable.True)
 | 
											
												
													
														|  |                  {
 |  |                  {
 | 
											
												
													
														|  | -                    if (_current == BooleanDisposable.True /* see IsDisposed */)
 |  | 
 | 
											
												
													
														|  | -                        return DefaultDisposable.Instance; // Don't leak the sentinel value.
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -                    return _current;
 |  | 
 | 
											
												
													
														|  | 
 |  | +                    a = DefaultDisposable.Instance;
 | 
											
												
													
														|  |                  }
 |  |                  }
 | 
											
												
													
														|  | 
 |  | +                return a;
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |              set
 |  |              set
 | 
											
												
													
														|  |              {
 |  |              {
 | 
											
												
													
														|  | -                var shouldDispose = false;
 |  | 
 | 
											
												
													
														|  | -                lock (_gate)
 |  | 
 | 
											
												
													
														|  | 
 |  | +                // Let's read the current value atomically (also prevents reordering).
 | 
											
												
													
														|  | 
 |  | +                var old = Volatile.Read(ref _current);
 | 
											
												
													
														|  | 
 |  | +                for (;;)
 | 
											
												
													
														|  |                  {
 |  |                  {
 | 
											
												
													
														|  | -                    shouldDispose = (_current == BooleanDisposable.True /* see IsDisposed */);
 |  | 
 | 
											
												
													
														|  | -                    if (!shouldDispose)
 |  | 
 | 
											
												
													
														|  | 
 |  | +                    // If it is the disposed instance, dispose the value.
 | 
											
												
													
														|  | 
 |  | +                    if (old == BooleanDisposable.True)
 | 
											
												
													
														|  |                      {
 |  |                      {
 | 
											
												
													
														|  | -                        _current = value;
 |  | 
 | 
											
												
													
														|  | 
 |  | +                        value?.Dispose();
 | 
											
												
													
														|  | 
 |  | +                        return;
 | 
											
												
													
														|  |                      }
 |  |                      }
 | 
											
												
													
														|  | 
 |  | +                    // Atomically swap in the new value and get back the old.
 | 
											
												
													
														|  | 
 |  | +                    var b = Interlocked.CompareExchange(ref _current, value, old);
 | 
											
												
													
														|  | 
 |  | +                    // If the old and new are the same, the swap was successful and we can quit
 | 
											
												
													
														|  | 
 |  | +                    if (old == b)
 | 
											
												
													
														|  | 
 |  | +                    {
 | 
											
												
													
														|  | 
 |  | +                        return;
 | 
											
												
													
														|  | 
 |  | +                    }
 | 
											
												
													
														|  | 
 |  | +                    // Otherwise, make the old reference the current and retry.
 | 
											
												
													
														|  | 
 |  | +                    old = b;
 | 
											
												
													
														|  |                  }
 |  |                  }
 | 
											
												
													
														|  | -                if (shouldDispose && value != null)
 |  | 
 | 
											
												
													
														|  | -                    value.Dispose();
 |  | 
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -74,19 +81,20 @@ namespace System.Reactive.Disposables
 | 
											
												
													
														|  |          /// </summary>
 |  |          /// </summary>
 | 
											
												
													
														|  |          public void Dispose()
 |  |          public void Dispose()
 | 
											
												
													
														|  |          {
 |  |          {
 | 
											
												
													
														|  | -            var old = default(IDisposable);
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -            lock (_gate)
 |  | 
 | 
											
												
													
														|  | 
 |  | +            // Read the current atomically.
 | 
											
												
													
														|  | 
 |  | +            var a = Volatile.Read(ref _current);
 | 
											
												
													
														|  | 
 |  | +            // If it is the disposed instance, don't bother further.
 | 
											
												
													
														|  | 
 |  | +            if (a != BooleanDisposable.True)
 | 
											
												
													
														|  |              {
 |  |              {
 | 
											
												
													
														|  | -                if (_current != BooleanDisposable.True /* see IsDisposed */)
 |  | 
 | 
											
												
													
														|  | 
 |  | +                // Atomically swap in the disposed instance.
 | 
											
												
													
														|  | 
 |  | +                a = Interlocked.Exchange(ref _current, BooleanDisposable.True);
 | 
											
												
													
														|  | 
 |  | +                // It is possible there was a concurrent Dispose call so don't need to call Dispose()
 | 
											
												
													
														|  | 
 |  | +                // on DISPOSED
 | 
											
												
													
														|  | 
 |  | +                if (a != BooleanDisposable.True)
 | 
											
												
													
														|  |                  {
 |  |                  {
 | 
											
												
													
														|  | -                    old = _current;
 |  | 
 | 
											
												
													
														|  | -                    _current = BooleanDisposable.True /* see IsDisposed */;
 |  | 
 | 
											
												
													
														|  | 
 |  | +                    a?.Dispose();
 | 
											
												
													
														|  |                  }
 |  |                  }
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -            if (old != null)
 |  | 
 | 
											
												
													
														|  | -                old.Dispose();
 |  | 
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  }
 |  |  }
 |