Skip to content

Conversation

akarnokd
Copy link
Collaborator

Optimize the blocking First and Last (== Wait) by inlining the various classes into one container and not waiting if the operation terminated by the time Subscribe returned.

{
evt.Set();
})))
consumer.SetUpstream(d);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? Any completion will unblock the consumer and d will be disposed by the using-block.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unblocking may happen any time later and the upstream would still push items to be dropped unnecessarily.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any time later, how?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pausing and resuming a thread takes time, about 5 microseconds on Windows based on my experience. During this time, the source may generate anything between zero to 1000 items, all of them wasting resources.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, you're taking that into account.

namespace System.Reactive.Linq.ObservableImpl
{

internal abstract class BaseBlocking<T> : CountdownEvent, IObserver<T>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CountdownEvent vs. ManualResetEvent ?

Copy link
Collaborator Author

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.

internal bool _hasValue;
internal Exception _error;

int once;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above, ManualResetEvent would save this field.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allocation vs an atomic operation tradeoff. If Signal wouldn't throw by default, we'd not need this.

{
this._value = value;
this._hasValue = true;
Disposable.TryDispose(ref _upstream);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no check here (or, why check above) ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnNext is called in a serialized fashion and a previous call already set _hasValue to true. Checking that upfront guarantees this executes at most once, but OnCompleted may still run regardless of _upstream disposed so that we want to avoid doing again there.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the order (Unblock, Dispose) reversed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No practical reason other than the reverse will race the upstream disposition with the using disposing.

@danielcweber
Copy link
Collaborator

I'm thinking about whether CountdownEvent.Dispose should be overridden to dispose the upstream, not that it would conceptually be required but simply because there is the disposable field and the instance is disposable.

@akarnokd
Copy link
Collaborator Author

Calling CountdownEvent.Dispose breaks the CountdownEvent and it cannot be unblocked after that. Methods will start throwing ObjectDisposedException as well.

Disposable.TryDispose(ref _upstream);
}

public override void OnError(Exception error)
Copy link
Collaborator

@danielcweber danielcweber Jun 14, 2018

Choose a reason for hiding this comment

The 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 ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least push some of the code into the base class.

@danielcweber
Copy link
Collaborator

Calling CountdownEvent.Dispose breaks the CountdownEvent

What I mean is, if CountdownEvent.Dispose is called, should the upstream be disposed?

@akarnokd
Copy link
Collaborator Author

akarnokd commented Jun 14, 2018

It can't be called here as the class/instance is not exposed.

@danielcweber
Copy link
Collaborator

Still, BaseBlocking implements IDisposable and holds a disposable, it should override and dispose of the upstream.

@akarnokd
Copy link
Collaborator Author

Why? BaseBlocking should never be disposed and can't be disposed. It is not returned as IDisposable anywhere.

@danielcweber
Copy link
Collaborator

Maybe it should be put in a using block then and honour that it implements IDisposable.

@akarnokd
Copy link
Collaborator Author

No. It makes no sense in this context. You dispose the subscription returned by Subscribe in using.

@danielcweber
Copy link
Collaborator

Thinking about that, it might definitely make sense since the CountdownEvent probably holds onto an unmanaged resource and should be disposed as soon you're done with it.

@danielcweber
Copy link
Collaborator

WaitAndSetOnce wrapped a ManualResetEvent before and it's in a using-block.

@akarnokd akarnokd closed this Jun 14, 2018
@akarnokd akarnokd reopened this Jun 14, 2018
@danielcweber danielcweber merged commit 8f67a95 into dotnet:master Jun 14, 2018
@akarnokd akarnokd deleted the FirstLastOptimize branch June 26, 2018 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants