I am using Reactive Extensions to create anonymous Observable to perform web request, post-process it and return some value. This technique helped me to separate the mechanics of value acquisition from other classes that use these values. Web requests are performed frequently, so there’s a timer that constructs and invokes requests using this technique. I build Observable as following:
Observable.FromAsyncPattern<WebResponse>(getRequest.BeginGetResponse, getRequest.EndGetResponse)
Actually, endpoint is not always available. In the case of web request timeout callbacks specified in Subscribe() methods are never called. I’m handling this scenario with Timeout() Rx extension. Also I’m doing post-processing of request result. Here’s piece of my emulation code that I implemented to address the issue:
var request = HttpWebRequest.Create("http://10.5.5.137/unreachable_endpoint");
var obs = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)();
var disposable = obs.Select(res => res.ContentLength).Timeout(TimeSpan.FromSeconds(2)).Subscribe(...)
Then I implemented disposing the return value of Subscribe() method to unsubscribe callback delegates from the Observable. Actually I have a collection of triples <IObservable obs, IDisposable disp, bool RequestComplete>. Subscribe() method callbacks set RequestComplete = true and over time all disposables get Disposed if they are not needed anymore, i.e. callbacks have already been invoked. Everything seemed to be fine, but I noticed that the collection is constantly growing and I don’t know why.
So I implemented the emulation of this situation. This code doesn’t have disposing part – I replaced it with simple request counter:
public static class RequestCounter
{
private static object syncRoot = new object();
private static int count = 0;
public static void Increment()
{
lock (syncRoot)
{
count++;
}
}
public static void Decrement()
{
lock (syncRoot)
{
count--;
}
}
public static int RequestCount
{
get { return count; }
}
}
And here’s the test itself:
public void RunTest()
{
while (true)
{
var request = HttpWebRequest.Create("http://10.5.5.137/unreachable_endpoint");
var obs = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)();
RequestCounter.Increment();
var disposable = obs.Select(res => res.ContentLength).Timeout(TimeSpan.FromSeconds(2)).Subscribe(
res =>
{
RequestCounter.Decrement();
Console.WriteLine("Pending requests count: {0}", RequestCounter.RequestCount);
},
ex =>
{
RequestCounter.Decrement();
Console.WriteLine("Pending requests count: {0}", RequestCounter.RequestCount);
},
() =>
{
});
}
}
Before web request counter gets incremented. After request timeout it gets decremented. But the request counter grows infinite. Can You explain me what am I missing?
The subscription to the observable sequence is asynchronous. Your while loop keeps going as fast as it can. Increments will outweigh the delayed decrements.
If you’re using Rx v2.0 with .NET 4.5, you could use await to run your loop and proceed to the following iteration when the previous request completed:
The bigger question is why you’re trying to dispose the subscriptions if the requests completed? Rx cleans up after itself when a sequence reaches a terminal state (OnError, OnCompleted), so it seems you could simply drop the IDisposable objects on the floor and not worry about those.