-
Notifications
You must be signed in to change notification settings - Fork 770
4.x: Improve blocking First & Last operators #590
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bd709f2
b0a7357
9c1a4ec
ba7a3bc
625ecfd
d142d0e
71b48eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// 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; | ||
using System.Collections.Generic; | ||
using System.Reactive.Disposables; | ||
using System.Text; | ||
using System.Threading; | ||
|
||
namespace System.Reactive.Linq.ObservableImpl | ||
{ | ||
|
||
internal abstract class BaseBlocking<T> : CountdownEvent, IObserver<T> | ||
{ | ||
protected IDisposable _upstream; | ||
|
||
internal T _value; | ||
internal bool _hasValue; | ||
internal Exception _error; | ||
|
||
int once; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above, ManualResetEvent would save this field. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allocation vs an atomic operation tradeoff. If |
||
|
||
internal BaseBlocking() : base(1) { } | ||
|
||
internal void SetUpstream(IDisposable d) | ||
{ | ||
Disposable.SetSingle(ref _upstream, d); | ||
} | ||
|
||
protected void Unblock() | ||
{ | ||
if (Interlocked.CompareExchange(ref once, 1, 0) == 0) | ||
{ | ||
Signal(); | ||
} | ||
} | ||
|
||
public abstract void OnCompleted(); | ||
public virtual void OnError(Exception error) | ||
{ | ||
_value = default; | ||
_error = error; | ||
Unblock(); | ||
} | ||
public abstract void OnNext(T value); | ||
|
||
public new void Dispose() | ||
{ | ||
base.Dispose(); | ||
if (!Disposable.GetIsDisposed(ref _upstream)) | ||
{ | ||
Disposable.TryDispose(ref _upstream); | ||
} | ||
} | ||
} | ||
|
||
internal sealed class FirstBlocking<T> : BaseBlocking<T> | ||
{ | ||
internal FirstBlocking() : base() { } | ||
|
||
public override void OnCompleted() | ||
{ | ||
Unblock(); | ||
if (!Disposable.GetIsDisposed(ref _upstream)) | ||
{ | ||
Disposable.TryDispose(ref _upstream); | ||
} | ||
} | ||
|
||
public override void OnError(Exception error) | ||
{ | ||
base.OnError(error); | ||
if (!Disposable.GetIsDisposed(ref _upstream)) | ||
{ | ||
Disposable.TryDispose(ref _upstream); | ||
} | ||
} | ||
|
||
public override void OnNext(T value) | ||
{ | ||
if (!_hasValue) | ||
{ | ||
_value = value; | ||
_hasValue = true; | ||
Disposable.TryDispose(ref _upstream); | ||
Unblock(); | ||
} | ||
} | ||
} | ||
|
||
internal sealed class LastBlocking<T> : BaseBlocking<T> | ||
{ | ||
internal LastBlocking() : base() { } | ||
|
||
public override void OnCompleted() | ||
{ | ||
Unblock(); | ||
Disposable.TryDispose(ref _upstream); | ||
} | ||
|
||
public override void OnError(Exception error) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Save for the IsDisposed-check, the overriding code is almost identical. Enough reason to push it to the base-class ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Last doesn't need that dispose check so why have it there. First needs it so why penalize everybody else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At least push some of the code into the base class. |
||
{ | ||
base.OnError(error); | ||
Disposable.TryDispose(ref _upstream); | ||
} | ||
|
||
public override void OnNext(T value) | ||
{ | ||
_value = value; | ||
_hasValue = true; | ||
} | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CountdownEvent vs. ManualResetEvent ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ManualResetEvent
cannot be extended,CountdownEvent
can, saving on an allocation.