Skip to content

Commit 7f1bc2a

Browse files
authored
Merge pull request #1362 from dotnet/dev/bartde/split_disposable_public_internal
Split public and internal disposable stuff.
2 parents f99de54 + 39b9971 commit 7f1bc2a

File tree

2 files changed

+206
-195
lines changed

2 files changed

+206
-195
lines changed
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable disable
6+
7+
using System.Threading;
8+
9+
namespace System.Reactive.Disposables
10+
{
11+
internal enum TrySetSingleResult
12+
{
13+
Success,
14+
AlreadyAssigned,
15+
Disposed
16+
}
17+
18+
public static partial class Disposable
19+
{
20+
/// <summary>
21+
/// Gets the value stored in <paramref name="fieldRef" /> or a null if
22+
/// <paramref name="fieldRef" /> was already disposed.
23+
/// </summary>
24+
internal static IDisposable GetValue(ref IDisposable fieldRef)
25+
{
26+
var current = Volatile.Read(ref fieldRef);
27+
28+
return current == BooleanDisposable.True
29+
? null
30+
: current;
31+
}
32+
33+
/// <summary>
34+
/// Gets the value stored in <paramref name="fieldRef" /> or a no-op-Disposable if
35+
/// <paramref name="fieldRef" /> was already disposed.
36+
/// </summary>
37+
internal static IDisposable GetValueOrDefault(ref IDisposable fieldRef)
38+
{
39+
var current = Volatile.Read(ref fieldRef);
40+
41+
return current == BooleanDisposable.True
42+
? EmptyDisposable.Instance
43+
: current;
44+
}
45+
46+
/// <summary>
47+
/// Assigns <paramref name="value" /> to <paramref name="fieldRef" />.
48+
/// </summary>
49+
/// <returns>true if <paramref name="fieldRef" /> was assigned to <paramref name="value" /> and has not
50+
/// been assigned before.</returns>
51+
/// <returns>false if <paramref name="fieldRef" /> has been already disposed.</returns>
52+
/// <exception cref="InvalidOperationException"><paramref name="fieldRef" /> has already been assigned a value.</exception>
53+
internal static bool SetSingle(ref IDisposable fieldRef, IDisposable value)
54+
{
55+
var result = TrySetSingle(ref fieldRef, value);
56+
57+
if (result == TrySetSingleResult.AlreadyAssigned)
58+
{
59+
throw new InvalidOperationException(Strings_Core.DISPOSABLE_ALREADY_ASSIGNED);
60+
}
61+
62+
return result == TrySetSingleResult.Success;
63+
}
64+
65+
/// <summary>
66+
/// Tries to assign <paramref name="value" /> to <paramref name="fieldRef" />.
67+
/// </summary>
68+
/// <returns>A <see cref="TrySetSingleResult"/> value indicating the outcome of the operation.</returns>
69+
internal static TrySetSingleResult TrySetSingle(ref IDisposable fieldRef, IDisposable value)
70+
{
71+
var old = Interlocked.CompareExchange(ref fieldRef, value, null);
72+
if (old == null)
73+
{
74+
return TrySetSingleResult.Success;
75+
}
76+
77+
if (old != BooleanDisposable.True)
78+
{
79+
return TrySetSingleResult.AlreadyAssigned;
80+
}
81+
82+
value?.Dispose();
83+
return TrySetSingleResult.Disposed;
84+
}
85+
86+
/// <summary>
87+
/// Tries to assign <paramref name="value" /> to <paramref name="fieldRef" />. If <paramref name="fieldRef" />
88+
/// is not disposed and is assigned a different value, it will not be disposed.
89+
/// </summary>
90+
/// <returns>true if <paramref name="value" /> was successfully assigned to <paramref name="fieldRef" />.</returns>
91+
/// <returns>false <paramref name="fieldRef" /> has been disposed.</returns>
92+
internal static bool TrySetMultiple(ref IDisposable fieldRef, IDisposable value)
93+
{
94+
// Let's read the current value atomically (also prevents reordering).
95+
var old = Volatile.Read(ref fieldRef);
96+
97+
for (; ; )
98+
{
99+
// If it is the disposed instance, dispose the value.
100+
if (old == BooleanDisposable.True)
101+
{
102+
value?.Dispose();
103+
return false;
104+
}
105+
106+
// Atomically swap in the new value and get back the old.
107+
var b = Interlocked.CompareExchange(ref fieldRef, value, old);
108+
109+
// If the old and new are the same, the swap was successful and we can quit
110+
if (old == b)
111+
{
112+
return true;
113+
}
114+
115+
// Otherwise, make the old reference the current and retry.
116+
old = b;
117+
}
118+
}
119+
120+
/// <summary>
121+
/// Tries to assign <paramref name="value" /> to <paramref name="fieldRef" />. If <paramref name="fieldRef" />
122+
/// is not disposed and is assigned a different value, it will be disposed.
123+
/// </summary>
124+
/// <returns>true if <paramref name="value" /> was successfully assigned to <paramref name="fieldRef" />.</returns>
125+
/// <returns>false <paramref name="fieldRef" /> has been disposed.</returns>
126+
internal static bool TrySetSerial(ref IDisposable fieldRef, IDisposable value)
127+
{
128+
var copy = Volatile.Read(ref fieldRef);
129+
for (; ; )
130+
{
131+
if (copy == BooleanDisposable.True)
132+
{
133+
value?.Dispose();
134+
return false;
135+
}
136+
137+
var current = Interlocked.CompareExchange(ref fieldRef, value, copy);
138+
if (current == copy)
139+
{
140+
copy?.Dispose();
141+
return true;
142+
}
143+
144+
copy = current;
145+
}
146+
}
147+
148+
/// <summary>
149+
/// Gets a value indicating whether <paramref name="fieldRef" /> has been disposed.
150+
/// </summary>
151+
/// <returns>true if <paramref name="fieldRef" /> has been disposed.</returns>
152+
/// <returns>false if <paramref name="fieldRef" /> has not been disposed.</returns>
153+
internal static bool GetIsDisposed(ref IDisposable fieldRef)
154+
{
155+
// We use a sentinel value to indicate we've been disposed. This sentinel never leaks
156+
// to the outside world (see the Disposable property getter), so no-one can ever assign
157+
// this value to us manually.
158+
return Volatile.Read(ref fieldRef) == BooleanDisposable.True;
159+
}
160+
161+
/// <summary>
162+
/// Tries to dispose <paramref name="fieldRef" />.
163+
/// </summary>
164+
/// <returns>true if <paramref name="fieldRef" /> was not disposed previously and was successfully disposed.</returns>
165+
/// <returns>false if <paramref name="fieldRef" /> was disposed previously.</returns>
166+
internal static bool TryDispose(ref IDisposable fieldRef)
167+
{
168+
var old = Interlocked.Exchange(ref fieldRef, BooleanDisposable.True);
169+
170+
if (old == BooleanDisposable.True)
171+
{
172+
return false;
173+
}
174+
175+
old?.Dispose();
176+
return true;
177+
}
178+
179+
/// <summary>
180+
/// Disposes <paramref name="fieldRef" />.
181+
/// </summary>
182+
internal static void Dispose(ref IDisposable fieldRef)
183+
{
184+
var old = Interlocked.Exchange(ref fieldRef, BooleanDisposable.True);
185+
186+
if (old != BooleanDisposable.True)
187+
{
188+
old?.Dispose();
189+
}
190+
}
191+
192+
internal static bool TryRelease<TState>(ref IDisposable fieldRef, TState state, Action<IDisposable, TState> disposeAction)
193+
{
194+
var old = Interlocked.Exchange(ref fieldRef, BooleanDisposable.True);
195+
196+
if (old == BooleanDisposable.True)
197+
{
198+
return false;
199+
}
200+
201+
disposeAction(old, state);
202+
return true;
203+
}
204+
}
205+
}

0 commit comments

Comments
 (0)