Consider the following:
[Fact]
public void foo()
{
var result = new Subject<bool>();
var startCount = 0;
var completionCount = 0;
var obs = Observable
.Defer(() =>
{
++startCount;
return result.FirstAsync();
})
.Do(_ => ++completionCount)
.Publish()
.RefCount();
// pretend there are lots of subscribers at once
var s1 = obs.Subscribe();
var s2 = obs.Subscribe();
var s3 = obs.Subscribe();
// even so, we only expect to be started once
Assert.Equal(1, startCount);
Assert.Equal(0, completionCount);
// and we won't complete until the result ticks through
result.OnNext(true);
Assert.Equal(1, startCount);
Assert.Equal(1, completionCount);
s1.Dispose();
s2.Dispose();
s3.Dispose();
// now try exactly the same thing again
s1 = obs.Subscribe();
s2 = obs.Subscribe();
s3 = obs.Subscribe();
// startCount is 4 here instead of the expected 2!
Assert.Equal(2, startCount);
Assert.Equal(1, completionCount);
result.OnNext(true);
Assert.Equal(2, startCount);
Assert.Equal(2, completionCount);
s1.Dispose();
s2.Dispose();
s3.Dispose();
}
My understanding of Publish + RefCount is that a connection to the source is maintained as long as there is at least one subscriber. Once the last subscriber disconnects, any future subscriber will re-initiate the connection to the source.
As you can see in my test, everything works perfectly the first time through. But the second time, the deferred observable inside the pipeline is executed once for every new subscriber.
I can see via the debugger that for the first group of subscribers, obs._count (which counts subscribers) increases for each call to Subscribe. But for the second group of subscribers, it remains zero.
Why is this happening and what can I do to rectify my pipeline?
The answer from #user631090 is close, but incorrect, so I thought I'd answer myself.
It's because Publish will immediately complete new subscribers if the stream it published has itself completed. You can kind of see that in the diagram here:
But it would have been nice if the diagram included a subscriber after the underlying stream completes.
To add to the confusion, Defer is still called for new subscribers. But its return value is simply ignored by Publish because of the initial stream completing.
I'm as yet unable to come up with a way to implement my intended use case. I thought perhaps using Multicast rather than Publish, creating a new subject as necessary. But I haven't been able to achieve that yet. And it seems rather painful for what I would think is a common use case.
It's because the underlying observable result has already completed. So each new subscriber is just getting the OnCompleted callback.
If ObservableDefer was creating a new sequence each time or one that didn't complete you would see the desired behavior.
e.g.
return result.FirstAsync().Concat(Observable.Never<bool>());
You will need to remove the Assert.Equal(1, completionCount);
Related
I am reproducing my Rx issue with a simplified test case below. The test below hangs. I am sure it is a small, but fundamental, thing that I am missing, but can't put my finger on it.
public class Service
{
private ISubject<double> _subject = new Subject<double>();
public void Reset()
{
_subject.OnNext(0.0);
}
public IObservable<double> GetProgress()
{
return _subject;
}
}
public class ObTest
{
[Fact]
private async Task SimpleTest()
{
var service = new Service();
var result = service.GetProgress().Take(1);
var task = Task.Run(async () =>
{
service.Reset();
});
await result;
}
}
UPDATE
My attempt above was to simplify the problem a little and understand it. In my case GetProgress() is a merge of various Observables that publish the download progress, one of these Observables is a Subject<double> that publishes 0 everytime somebody calls a method to delete the download.
The race condition identified by Enigmativity and Theodor Zoulias may(??) happen in real life. I display a view which attempts to get the progress, however, quick fingers delete it just in time.
What I need to understand a bit more is if the download is started again (subscription has taken place by now, by virtue of displaying a view, which has already made the subscription) and somebody again deletes it.
public class Service
{
private ISubject<double> _deleteSubject = new Subject<double>();
public void Reset()
{
_deleteSubject.OnNext(0.0);
}
public IObservable<double> GetProgress()
{
return _deleteSubject.Merge(downloadProgress);
}
}
Your code isn't hanging. It's awaiting an observable that sometimes never gets a value.
You have a race condition.
The Task.Run is sometimes executing to completion before the await result creates the subscription to the observable - so it never sees the value.
Try this code instead:
private async Task SimpleTest()
{
var service = new Service();
var result = service.GetProgress().Take(1);
var awaiter = result.GetAwaiter();
var task = Task.Run(() =>
{
service.Reset();
});
await awaiter;
}
The line await result creates a subscription to the observable. The problem is that the notification _subject.OnNext(0.0) may occur before this subscription, in which case the value will pass unobserved, and the await result will continue waiting for a notification for ever. In this particular example the notification is always missed, at least in my PC, because the subscription is delayed for around 30 msec (measured with a Stopwatch), which is longer than the time needed for the task that resets the service to complete, probably because the JITer must load and compile some RX-related assembly. The situation changes when I do a warm-up by calling new Subject<int>().FirstAsync().Subscribe() before running the example. In that case the notification is observed almost always, and the hanging is avoided.
I can think of two robust solutions to this problem.
The solution suggested by Enigmativity, to create an awaitable subscription before starting the task that resets the service. This can be done with either GetAwaiter or ToTask.
To use a ReplaySubject<T> instead of a plain vanilla Subject<T>.
Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies.
The ReplaySubject will cache the value so that it can be observed by the future subscription, eliminating the race condition. You could initialize it with a bufferSize of 1 to minimize the memory footprint of the buffer.
I'm experiencing different behaviour with an TakeUntil and different implementations of IObservable as the parameter. I'm trying to understand WHY i get the different behaviour. Using Linqpad:
async Task Main()
{
var byTimer = true;
var ending = Observable.FromEvent<long>(
handler => endingHandler += handler,
handler => endingHandler -= handler);
var s = new Subject<long>();
using (var disposibleSub =
Observable
.Interval(TimeSpan.FromSeconds(.2))
.TakeUntil(byTimer ? Observable.Timer(TimeSpan.FromSeconds(1.5)) : ending)
.DumpLatest()
.Subscribe(Observer.Checked(s)))
{
if (endingHandler != null)
{
int r = Console.Read();
endingHandler?.Invoke(r);
}
var v = await s.Count();
Console.WriteLine("Count of items: {0}", v);
}
}
public event Action<long> endingHandler;
The one with the timer Count always returns the correct value. However, if I change it to use the FromEvent implementation, I always get 0. Obviously the difference is in the implementation of the two. I've also tried using a Subject implementation for the TakeUntil with the same results as the fromEvent.
The timer result is what I expected.
An explanation why would be appreciated! Thanks.
When byTimer is true then the ending observable never gets a subscription - remember that the observable pipeline is only instantiated when a subscription arrives - so in this case the handler => endingHandler += handler attach event code doesn't run and thus endingHandler is null. That means that the Console.Read() isn't called so the code drops immediately to var v = await s.Count(); and this then catches all of the values passing thru s.
However, when byTimer is false then endingHandler is not null so then Console.Read() is called. When the console is read you immediately invoke endingHandler which stops the observable and calls OnCompleted on the subject. So when it hits var v = await s.Count(); it immediately gets a completed signal, missing all of the previously produced values, and thus you get a count of zero.
If you change your code to be like this:
int r = Console.Read();
if (endingHandler != null)
{
endingHandler?.Invoke(r);
}
var v = await s.Count();
Then the two observables behave exactly the same way.
If you then also go and change the Subject to ReplaySubject then the code should behave as you were originally expecting it to.
I have a project where I need to send a status message every 10 seconds unless there's been an update in the meantime. Meaning, every time there would be an update, the timer would reset.
var res = Observable
.Interval(TimeSpan.FromSeconds(10))
.Where(_ => condition);
res.Subscribe(_ => Console.WriteLine("Status sent."));
Now I know that the "Where" will only be applied when the timer ends, so it doesn't help. But, I'm wondering if there's a way to reset the Interval; or to use a Timer() with a repeat.
This is pretty easy to implement using standard Rx operators.
What isn't clear from your question is exactly what an "update" is. I'm going to assume that you have some sort of observable that fires for every update or that you can create a subject that you'll call .OnNext(...) when there is an update. Without observable updates it is hard to know when to reset the timer.
So here's the code:
var update = new Subject<bool>();
var res =
update
.Select(x => Observable.Interval(TimeSpan.FromSeconds(10.0)))
.Switch();
res
.Subscribe(_ => Console.WriteLine("Status sent."));
update.OnNext(true);
The res query now waits until it gets a value from update and then it selects a new Observable.Interval. This means that after the Select the type is an IObservable<IObservable<long>>, so the .Switch() is required to turn it in to a IObservable<long>. .Switch() does this by only passing out values from the latest observed observable and disposing of any previous observables. In other words, for each update a new timer is started and the previous timer is cancelled. This means that if you have updates occurring more frequently than 10 seconds then the timer will never fire.
Now, if the res observable is an update in its own right, then you can do this:
res
.Subscribe(_ =>
{
update.OnNext(true);
Console.WriteLine("Status sent.");
});
That's fine - it still works, but for each timer firing res will create a new timer. It will mean that anything relying on your update observable/subject will still function correctly.
I keep this little helper method with me:
public static IObservable<long> CreateAutoResetInterval<TSource>(IObservable<TSource> resetter, TimeSpan timeSpan, bool immediate = false)
{
return resetter.Select(_ => immediate ? Observable.Interval(timeSpan).StartWith(0) : Observable.Interval(timeSpan)).Switch();
}
It's basically the same mechanism as Enigmativity's answer
I think you could also get away with using Throttle here. The purpose of Throttle is not to let elements though if another element is received within the given timespan. So in your case if an update message is received within 10 secs then don't send a status. See unit test below which uses 200 ticks as the throttle period.
[TestMethod]
public void Publish_Status_If_Nothing_Receieved()
{
//Arrange
var scheduler = new TestScheduler();
var statusObserver = scheduler.CreateObserver<Unit>();
var updateStream = scheduler.CreateColdObservable(OnNext(100, 1), OnNext(200, 2), OnNext(600, 3),
OnNext(700, 4));
var waitTime = TimeSpan.FromTicks(200);
//Act
updateStream.Throttle(waitTime, scheduler)
.Select(_ => Unit.Default)
.Subscribe(statusObserver);
//Verify no status received
scheduler.AdvanceTo(100);
Assert.AreEqual(0, statusObserver.Messages.Count);
//Verify no status received
scheduler.AdvanceTo(200);
Assert.AreEqual(0, statusObserver.Messages.Count);
//Assert status received
scheduler.AdvanceTo(400);
statusObserver.Messages.AssertEqual(OnNext(400, Unit.Default));
//Verify no more status received
scheduler.AdvanceTo(700);
statusObserver.Messages.AssertEqual(OnNext(400, Unit.Default));
}
I'm trying to create an async unit test for the project, but cannot understand how to wait for the async subject to complete:
[Test]
public async void MicroTest()
{
var value = 2;
var first = new AsyncSubject<int>();
var second = new AsyncSubject<int>();
first.Subscribe(_ =>
{
value = _;
second.OnCompleted();
});
first.OnNext(1);
// how to wait for the second subject to complete?
Assert.AreEqual(value, 1);
}
Sync version of this test is works well:
[Test]
public void MicroTest()
{
var value = 2;
var first = new Subject<int>();
var second = new Subject<int>();
first.Subscribe(_ =>
{
value = _;
second.OnCompleted();
});
first.OnNext(1);
Assert.AreEqual(value, 1);
}
AsyncSubject versus Subject
First off, it's worth pointing out that AsyncSubject<T> is not an asynchronous version of Subject<T>. Both are in fact free-threaded* (see footnote).
AsyncSubject is a specialization of Subject intended to be used to model an operation that completes asynchronously and returns a single result. It has two noteworthy features:
Only the last result is published
The result is cached and is available to observers subscribing after it has completed.
It is used internally in various places, including by the ToObservable() extension method defined on Task and Task<T>.
The issue with the test
Recall AsyncSubject<T> will only return the final result received. It does this by waiting for OnCompleted() so it knows what the final result is. Because you do not call OnCompleted() on first your test is flawed as the OnNext() handler - the lambda function passed in your Subscribe call - will never be invoked.
Additionally, it is invalid not to call OnNext() at least once on an AsyncSubject<T>, so when you call await second; you will get an InvalidOperationException if you haven't done this.
If you write your test as follows, all is well:
[Test]
public async void MicroTest()
{
var value = 2;
var first = new AsyncSubject<int>();
var second = new AsyncSubject<int>();
first.Subscribe(_ =>
{
// won't be called until an OnCompleted() has
// been invoked on first
value = _;
// you must send *some* value to second
second.OnNext(_);
second.OnCompleted();
});
first.OnNext(1);
// you must do this for OnNext handler to be called
first.OnCompleted();
// how to wait for the second subject to complete
await second;
Assert.AreEqual(value, 1);
}
About asynchronous tests
As a general rule I would avoid writing asynchronous tests that could wait forever. This gets particularly annoying when it causes resource drains on build servers. Use some kind of timeout e.g:
await second.Timeout(TimeSpan.FromSeconds(1));
No need to handle the exception since that is enough for the test to fail.
**I've borrowed this term from the COM lexicon. In this sense I mean that they, as with most of the Rx framework components, will generally run on whatever thread you happen to invoke their methods on. Being free-threaded doesn't necessarily mean being fully thread safe though. In particular, unlike AsyncSubject<T>, Subject<T> doesn't protect you from the Rx grammar violation of making overlapping calls to OnNext. Use Subject.Synchronize or Observable.Synchronize for this protection.*
I needed to alternate between two states with each state having a different interval time.
The best way I could think of doing this was to use Reactive Extensions' Observable.Generate
which is pretty awsome.
From what I read on msdn and other sites, Observable.Finally() should fire if the
observable "terminates gracefully or exceptionally". I was testing the following code
(in LINQPad) to see how it works, but I can not get .Finall() to fire at all.
var ia = TimeSpan.FromSeconds(1);
var ib = TimeSpan.FromSeconds(.2);
var start = DateTime.UtcNow;
var ct = new CancellationTokenSource();
var o = Observable.Generate(
true,
// s => !ct.IsCancellationRequested,
s => (DateTime.UtcNow-start) < TimeSpan.FromSeconds(3) && !ct.IsCancellationRequested,
s => !s,
s => s ? "on" : "off",
s => s? ib : ia)
// .TakeUntil(start+TimeSpan.FromSeconds(3))
.Concat(Observable.Return("end"));
o.Subscribe( s=> s.Dump(), ct.Token);
var t = o.ToTask(ct.Token);
t.ContinueWith(x => x.Dump("done"));
o.Finally(() => "finallY".Dump()); // never gets called?
Thread.Sleep(10000);
ct.Cancel();
If I make Thread.Sleep 10s, the observable sequence finishes and the Task.ContinueWith fires,
but not .Finally().
If I make Thread.Sleep 2s, the observable sequence is canceled and the Task.ContinueWith again fires,
but not .Finally().
Why not?
Look at the return type of the Finally method; should give you a hint. Just like the Concat method returns a new IObservable with the new sequence concatenated to it, but doesn't change the original, the Finally method returns a new IObservable that has that final action, but you're subscribing to the original IObservable. Put the following line in front of your Subscribe call and it'll work.
o = o.Finally(() => "finallY".Dump());
I agree it's an odd API choice though; I'd think of Finally as being more akin to Subscribe than to Concat. You're subscribing to the finally "event"; it's odd that the API forces you to create a completely new IObservable and then subscribe to that just to get the Finally thing to happen. Plus it allows a potential error (made evident if we use the function in your question) that if you subscribe twice to that new IObservable, your Finally function will execute twice. So you have to make sure that one of your subscriptions is on the "finallied" IObservable and the others are all on the original. Just seems unusual.
I guess the way to think about it is that Finally isn't meant to modify the observable, but rather to modify the subscription itself. i.e., they don't expect you typically to make openly-accessible named observables that have Finally things (var o = Observable.[...].Finally(...);) rather it's meant to go inline with the subscription call itself (var subscription = o.Finally(...).Subscribe(...);)