Skip to content

Commit 0c5a53f

Browse files
authored
Align safe observers (#603)
1 parent da20177 commit 0c5a53f

File tree

8 files changed

+63
-120
lines changed

8 files changed

+63
-120
lines changed

Rx.NET/Source/src/System.Reactive/AnonymousSafeObserver.cs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@ namespace System.Reactive
1919
/// that accept delegates for the On* handlers. By doing the fusion, we make the call stack depth shorter which
2020
/// helps debugging and some performance.
2121
/// </summary>
22-
internal sealed class AnonymousSafeObserver<T> : ISafeObserver<T>
22+
internal sealed class AnonymousSafeObserver<T> : SafeObserver<T>
2323
{
2424
private readonly Action<T> _onNext;
2525
private readonly Action<Exception> _onError;
2626
private readonly Action _onCompleted;
27-
private IDisposable _disposable;
2827

2928
private int isStopped;
3029

@@ -35,7 +34,7 @@ public AnonymousSafeObserver(Action<T> onNext, Action<Exception> onError, Action
3534
_onCompleted = onCompleted;
3635
}
3736

38-
public void OnNext(T value)
37+
public override void OnNext(T value)
3938
{
4039
if (isStopped == 0)
4140
{
@@ -53,7 +52,7 @@ public void OnNext(T value)
5352
}
5453
}
5554

56-
public void OnError(Exception error)
55+
public override void OnError(Exception error)
5756
{
5857
if (Interlocked.Exchange(ref isStopped, 1) == 0)
5958
{
@@ -64,7 +63,7 @@ public void OnError(Exception error)
6463
}
6564
}
6665

67-
public void OnCompleted()
66+
public override void OnCompleted()
6867
{
6968
if (Interlocked.Exchange(ref isStopped, 1) == 0)
7069
{
@@ -74,15 +73,5 @@ public void OnCompleted()
7473
}
7574
}
7675
}
77-
78-
public void SetResource(IDisposable resource)
79-
{
80-
Disposable.SetSingle(ref _disposable, resource);
81-
}
82-
83-
public void Dispose()
84-
{
85-
Disposable.TryDispose(ref _disposable);
86-
}
8776
}
8877
}

Rx.NET/Source/src/System.Reactive/Internal/AutoDetachObserver.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Reactive.Disposables;
6-
using System.Threading;
76

87
namespace System.Reactive
98
{
10-
internal sealed class AutoDetachObserver<T> : ObserverBase<T>
9+
internal sealed class AutoDetachObserver<T> : ObserverBase<T>, ISafeObserver<T>
1110
{
1211
private readonly IObserver<T> _observer;
1312

@@ -18,9 +17,9 @@ public AutoDetachObserver(IObserver<T> observer)
1817
_observer = observer;
1918
}
2019

21-
public IDisposable Disposable
20+
public void SetResource(IDisposable resource)
2221
{
23-
set => Disposables.Disposable.SetSingle(ref _disposable, value);
22+
Disposable.SetSingle(ref _disposable, resource);
2423
}
2524

2625
protected override void OnNextCore(T value)

Rx.NET/Source/src/System.Reactive/Internal/ObserverWithToken.cs

Lines changed: 0 additions & 60 deletions
This file was deleted.

Rx.NET/Source/src/System.Reactive/Internal/Producer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public IDisposable SubscribeRaw(IObserver<TSource> observer, bool enableSafeguar
4646
//
4747
if (enableSafeguard)
4848
{
49-
var safeObserver = SafeObserver<TSource>.Create(observer);
49+
var safeObserver = SafeObserver<TSource>.Wrap(observer);
5050
safeObserver.SetResource(subscription);
5151
observer = safeObserver;
5252
}
@@ -100,7 +100,7 @@ public IDisposable SubscribeRaw(IObserver<TTarget> observer, bool enableSafeguar
100100
//
101101
if (enableSafeguard)
102102
{
103-
observer = safeObserver = SafeObserver<TTarget>.Create(observer);
103+
observer = safeObserver = SafeObserver<TTarget>.Wrap(observer);
104104
}
105105

106106
var sink = CreateSink(observer);

Rx.NET/Source/src/System.Reactive/Internal/SafeObserver.cs

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,70 +11,85 @@ namespace System.Reactive
1111
// its implementation aspects.
1212
//
1313

14-
internal sealed class SafeObserver<TSource> : ISafeObserver<TSource>
14+
internal abstract class SafeObserver<TSource> : ISafeObserver<TSource>
1515
{
16-
private readonly IObserver<TSource> _observer;
17-
18-
private IDisposable _disposable;
19-
20-
public static ISafeObserver<TSource> Create(IObserver<TSource> observer)
16+
private sealed class WrappingSafeObserver : SafeObserver<TSource>
2117
{
22-
if (observer is AnonymousObserver<TSource> a)
18+
private readonly IObserver<TSource> _observer;
19+
20+
public WrappingSafeObserver(IObserver<TSource> observer)
2321
{
24-
return a.MakeSafe();
22+
_observer = observer;
2523
}
26-
else
24+
25+
public override void OnNext(TSource value)
2726
{
28-
return new SafeObserver<TSource>(observer);
27+
var __noError = false;
28+
try
29+
{
30+
_observer.OnNext(value);
31+
__noError = true;
32+
}
33+
finally
34+
{
35+
if (!__noError)
36+
{
37+
Dispose();
38+
}
39+
}
2940
}
30-
}
3141

32-
private SafeObserver(IObserver<TSource> observer)
33-
{
34-
_observer = observer;
35-
}
36-
37-
public void OnNext(TSource value)
38-
{
39-
var __noError = false;
40-
try
42+
public override void OnError(Exception error)
4143
{
42-
_observer.OnNext(value);
43-
__noError = true;
44+
using (this)
45+
{
46+
_observer.OnError(error);
47+
}
4448
}
45-
finally
49+
50+
public override void OnCompleted()
4651
{
47-
if (!__noError)
52+
using (this)
4853
{
49-
Dispose();
54+
_observer.OnCompleted();
5055
}
5156
}
5257
}
5358

54-
public void OnError(Exception error)
59+
public static ISafeObserver<TSource> Wrap(IObserver<TSource> observer)
5560
{
56-
using (this)
61+
if (observer is AnonymousObserver<TSource> a)
5762
{
58-
_observer.OnError(error);
63+
return a.MakeSafe();
5964
}
60-
}
61-
62-
public void OnCompleted()
63-
{
64-
using (this)
65+
else
6566
{
66-
_observer.OnCompleted();
67+
return new WrappingSafeObserver(observer);
6768
}
6869
}
6970

71+
private IDisposable _disposable;
72+
73+
public abstract void OnNext(TSource value);
74+
75+
public abstract void OnError(Exception error);
76+
77+
public abstract void OnCompleted();
78+
7079
public void SetResource(IDisposable resource)
7180
{
7281
Disposable.SetSingle(ref _disposable, resource);
7382
}
7483

7584
public void Dispose()
7685
{
77-
Disposable.TryDispose(ref _disposable);
86+
Dispose(true);
87+
}
88+
89+
protected virtual void Dispose(bool disposing)
90+
{
91+
if (disposing)
92+
Disposable.TryDispose(ref _disposable);
7893
}
7994
}
8095
}

Rx.NET/Source/src/System.Reactive/Observable.Extensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ private static void Subscribe_<T>(this IObservable<T> source, IObserver<T> obser
259259
{
260260
if (!token.IsCancellationRequested)
261261
{
262-
var consumer = new ObserverWithToken<T>(observer);
262+
var consumer = SafeObserver<T>.Wrap(observer);
263263

264264
//
265265
// [OK] Use of unsafe Subscribe: exception during Subscribe doesn't orphan CancellationTokenRegistration.

Rx.NET/Source/src/System.Reactive/ObservableBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public IDisposable Subscribe(IObserver<T> observer)
5454
{
5555
try
5656
{
57-
autoDetachObserver.Disposable = SubscribeCore(autoDetachObserver);
57+
autoDetachObserver.SetResource(SubscribeCore(autoDetachObserver));
5858
}
5959
catch (Exception exception)
6060
{
@@ -79,7 +79,7 @@ private IDisposable ScheduledSubscribe(IScheduler _, AutoDetachObserver<T> autoD
7979
{
8080
try
8181
{
82-
autoDetachObserver.Disposable = SubscribeCore(autoDetachObserver);
82+
autoDetachObserver.SetResource(SubscribeCore(autoDetachObserver));
8383
}
8484
catch (Exception exception)
8585
{

Rx.NET/Source/src/System.Reactive/Platforms/Desktop/Linq/QueryLanguage.Remoting.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public SerializableObservable(RemotableObservable<T> remotableObservable)
8585

8686
public IDisposable Subscribe(IObserver<T> observer)
8787
{
88-
var consumer = new ObserverWithToken<T>(observer);
88+
var consumer = SafeObserver<T>.Wrap(observer);
8989

9090
//
9191
// [OK] Use of unsafe Subscribe: non-pretentious transparent wrapping through remoting; exception coming from the remote object is not re-routed.

0 commit comments

Comments
 (0)