Skip to content
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

What is ObserveOnTimeProvider intended / useful for? #261

Open
JaggerJo opened this issue Oct 22, 2024 · 3 comments
Open

What is ObserveOnTimeProvider intended / useful for? #261

JaggerJo opened this issue Oct 22, 2024 · 3 comments

Comments

@JaggerJo
Copy link

JaggerJo commented Oct 22, 2024

Really interesting project. We're heavily using RxNet and could benefit from a lower overhead implementation.

I was looking through the sources and especially into how ObserveOn(..) is implemented. After looking into it I don't get what ObserveOnTimeProvider is for. An explanation would be appreciated.

ObserveOnTimeProvider is only created when ObserveOn is called with the non system TimeProvider.

The internal _Observer class of the ObserveOnTimeProvider creates a stopped timer that (It seems) never elapses.

public static ITimer CreateStoppedTimer(this TimeProvider timeProvider, TimerCallback timerCallback, object? state)
{
return timeProvider.CreateTimer(timerCallback, state, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
}

After values arrive in the _Observer this extension method is called on the timer:

public static void RestartImmediately(this ITimer timer)
{
timer.Change(TimeSpan.Zero, Timeout.InfiniteTimeSpan);
}

According to the docs this timer is invoked immediately but never called periodically.

In this test case the FakeTimeProvider is not advanced, but the elements still end up in the output list.

public void TimeProvider()
{
var fakeTime = new FakeTimeProvider();
var publisher = new Subject<int>();
using var list = publisher.ObserveOn(fakeTime).ToLiveList();
publisher.OnNext(10);
publisher.OnNext(20);
publisher.OnNext(30);
list.AssertEqual([10, 20, 30]);
publisher.OnCompleted();
list.AssertIsCompleted();
}

What is the ObserveOnTimeProvider used for if it just immediately passes on messages, even if time stands still from the timers perspective?

@neuecc
Copy link
Member

neuecc commented Oct 23, 2024

TimeProvider serves the role of IScheduler in RxNet.
In other words, ObserverOnTimeProvider is equivalent to ObserveOnScheduler.
For example, if you pass WpfDispatcherTimerProvider (which is the TimeProvider implementation for WPF provided by R3), you can place values on the Dispatcher (UI thread).

@JaggerJo
Copy link
Author

@neuecc Thanks for the quick reply.

Might be a silly question, but why do you need a timer for it if you only ever use the run this callback ... portion? I really want to get it. I think I can learn something here :)

https://learn.microsoft.com/en-us/dotnet/api/system.timeprovider.createtimer?view=net-8.0#parameters

A delegate representing a method to be executed when the timer fires. The method specified for callback should be reentrant, as it may be invoked simultaneously on two threads if the timer fires again before or while a previous callback is still being handled.

@neuecc
Copy link
Member

neuecc commented Oct 23, 2024

First, generating and executing a Timer for each individual value must absolutely be avoided for performance reasons.
Therefore, it uses a single Timer and maintains a local Queue for when new values arrive during delegate execution.
ObserveOn is a feature that emits values in the execution context provided by the TimeProvider.
Thus, with SystemTimeProvider it's the ThreadPool, and with WpfDispatcherTimeProvider it's the Dispatcher.
In the case of FakeTimeProvider, since it doesn't have an execution context, it becomes Immediate.
Since this isn't about waiting for a specific time period, it's set to fire just once immediately.
By using Timer's Change method, we can execute the delegate once in the execution context each time without having to generate new Timers.
In RxNet terms, we're simulating something like IScheduler.Schedule(delegate) using Timer.

By the way, giving special treatment to TimeProvider.System by making it ObserveOnThreadPool is done for higher performance optimization.
Even if you were to CreateTimer with SystemTimeProvider, the behavior itself would be the same.

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

No branches or pull requests

2 participants