Skip to content

Commit 70f1dfd

Browse files
akarnokdOren Novotny
authored andcommitted
4.x: SerialDisposable to use lock free methods (#535)
* Lockfree SerialDisposable * Disposed SerialDisposable returns null from Disposable property
1 parent 742d5fa commit 70f1dfd

File tree

1 file changed

+27
-34
lines changed

1 file changed

+27
-34
lines changed

Rx.NET/Source/src/System.Reactive/Disposables/SerialDisposable.cs

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Threading;
6+
57
namespace System.Reactive.Disposables
68
{
79
/// <summary>
810
/// Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource.
911
/// </summary>
1012
public sealed class SerialDisposable : ICancelable
1113
{
12-
private readonly object _gate = new object();
1314
private IDisposable _current;
14-
private bool _disposed;
1515

1616
/// <summary>
17-
/// Initializes a new instance of the <see cref="SerialDisposable"/> class.
17+
/// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.SerialDisposable"/> class.
1818
/// </summary>
1919
public SerialDisposable()
2020
{
@@ -27,10 +27,10 @@ public bool IsDisposed
2727
{
2828
get
2929
{
30-
lock (_gate)
31-
{
32-
return _disposed;
33-
}
30+
// We use a sentinel value to indicate we've been disposed. This sentinel never leaks
31+
// to the outside world (see the Disposable property getter), so no-one can ever assign
32+
// this value to us manually.
33+
return Volatile.Read(ref _current) == BooleanDisposable.True;
3434
}
3535
}
3636

@@ -42,28 +42,32 @@ public IDisposable Disposable
4242
{
4343
get
4444
{
45-
return _current;
45+
var a = Volatile.Read(ref _current);
46+
// Don't leak the DISPOSED sentinel
47+
if (a == BooleanDisposable.True)
48+
{
49+
a = null;
50+
}
51+
return a;
4652
}
4753

4854
set
4955
{
50-
var shouldDispose = false;
51-
var old = default(IDisposable);
52-
lock (_gate)
56+
var copy = Volatile.Read(ref _current);
57+
for (;;)
5358
{
54-
shouldDispose = _disposed;
55-
if (!shouldDispose)
59+
if (copy == BooleanDisposable.True)
5660
{
57-
old = _current;
58-
_current = value;
61+
value?.Dispose();
62+
return;
5963
}
60-
}
61-
62-
old?.Dispose();
63-
64-
if (shouldDispose)
65-
{
66-
value?.Dispose();
64+
var current = Interlocked.CompareExchange(ref _current, value, copy);
65+
if (current == copy)
66+
{
67+
copy?.Dispose();
68+
return;
69+
}
70+
copy = current;
6771
}
6872
}
6973
}
@@ -73,18 +77,7 @@ public IDisposable Disposable
7377
/// </summary>
7478
public void Dispose()
7579
{
76-
var old = default(IDisposable);
77-
78-
lock (_gate)
79-
{
80-
if (!_disposed)
81-
{
82-
_disposed = true;
83-
old = _current;
84-
_current = null;
85-
}
86-
}
87-
80+
var old = Interlocked.Exchange(ref _current, BooleanDisposable.True);
8881
old?.Dispose();
8982
}
9083
}

0 commit comments

Comments
 (0)