Skip to content

Commit

Permalink
4.x: SerialDisposable to use lock free methods (#535)
Browse files Browse the repository at this point in the history
* Lockfree SerialDisposable

* Disposed SerialDisposable returns null from Disposable property
  • Loading branch information
akarnokd authored and Oren Novotny committed May 26, 2018
1 parent 742d5fa commit 70f1dfd
Showing 1 changed file with 27 additions and 34 deletions.
61 changes: 27 additions & 34 deletions Rx.NET/Source/src/System.Reactive/Disposables/SerialDisposable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
// 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.

using System.Threading;

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="SerialDisposable"/> class.
/// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.SerialDisposable"/> class.
/// </summary>
public SerialDisposable()
{
Expand All @@ -27,10 +27,10 @@ public bool IsDisposed
{
get
{
lock (_gate)
{
return _disposed;
}
// 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;
}
}

Expand All @@ -42,28 +42,32 @@ public IDisposable Disposable
{
get
{
return _current;
var a = Volatile.Read(ref _current);
// Don't leak the DISPOSED sentinel
if (a == BooleanDisposable.True)
{
a = null;
}
return a;
}

set
{
var shouldDispose = false;
var old = default(IDisposable);
lock (_gate)
var copy = Volatile.Read(ref _current);
for (;;)
{
shouldDispose = _disposed;
if (!shouldDispose)
if (copy == BooleanDisposable.True)
{
old = _current;
_current = value;
value?.Dispose();
return;
}
}

old?.Dispose();

if (shouldDispose)
{
value?.Dispose();
var current = Interlocked.CompareExchange(ref _current, value, copy);
if (current == copy)
{
copy?.Dispose();
return;
}
copy = current;
}
}
}
Expand All @@ -73,18 +77,7 @@ public IDisposable Disposable
/// </summary>
public void Dispose()
{
var old = default(IDisposable);

lock (_gate)
{
if (!_disposed)
{
_disposed = true;
old = _current;
_current = null;
}
}

var old = Interlocked.Exchange(ref _current, BooleanDisposable.True);
old?.Dispose();
}
}
Expand Down

0 comments on commit 70f1dfd

Please sign in to comment.