Tuesday 8 July 2014

Polling with Timeout in .NET 4.5

A couple of times recently I have needed to be able to wait (asynchronously) for something to happen, but also permit a timeout to occur. I wrote a version of this many moons ago that used wait handles and was pretty difficult to understand, now with async support in .NET I decided to rewrite it.

The basic premise is that I want to be able to call the following…

while (not done and not timedout)
{
if (signalFunc())
return (dataFunc());
else
wait a bit;
}


The signalFunc() is called to check that something has happened. The dataFunc() returns the actual data you are waiting for.

One of the places I need this is when trying to make a fully asynchronous pipeline in Azure look like a synchronous function to the caller. A request comes in, gets queued, gets processed, gets queued again, and gets processed again before being complete – and I want a way to hide all of this complexity from a caller so that we can provide an API that looks synchronous to the caller, but is actually asynchronous internally.


In this case I add a sentinel value to the database that the signalFunc() looks for, and then push the request into my Azure pipeline. The signalFunc() is polled repeatedly and, once the request has passed al the way through my request pipeline it updates the sentinel in the database. The signalFunc() will then report true and I can then execute the dataFunc() to find whatever it is the caller needs and return it to them.


So, after having rewritten this using async I have arrived at the following API…

public static Task<T> PollWithTimeoutAsync<T>(Func<bool> signalFunc, Func<T> dataFunc, int millisecondsBetweenPolls, int millisecondsToTimeout)



There’s also another one that includes a cancellation token. The signalFunc() is called repeatedly (with a delay of millisecondsBetweenPolls) and if true the dataFunc() is called to provide the data. The whole operation waits at most millisecondsToTimeout before throwing a TimeoutException.


The full code is as follows…

public static async Task<T> PollWithTimeoutAsync<T>(Func<bool> signalFunc, Func<T> dataFunc, int millisecondsBetweenPolls, int millisecondsToTimeout)
{
if (null == signalFunc) throw new ArgumentNullException("signalFunc");
if (null == dataFunc) throw new ArgumentNullException("dataFunc");
if (millisecondsBetweenPolls >= millisecondsToTimeout) throw new ArgumentException("The millisecondsBetweenPolls should be less than millisecondsToTimeout");

using (var cts = new CancellationTokenSource(millisecondsToTimeout))
{
bool done = signalFunc();

while (!done)
{
try
{
await Task.Delay(millisecondsBetweenPolls, cts.Token);
}
catch (TaskCanceledException)
{
throw new TimeoutException();
}

done = signalFunc();
}

return dataFunc();
}
}



It’s fairly terse (isn’t all good code like that?) and uses a feature of CancellationTokenSource which helps out a lot here, as the override I have used ensures that the token is cancelled after the period defined by the millisecondsToTimeout parameter. So, I setup a cancellation token source to go off in a few seconds, then loop calling the signalFunc() and if that reports false, use Task.Delay() to wait for a while before polling again. The beauty of Task.Delay is that it’s also cancellable by using a cancellation token, so if I’m in the middle of waiting and the overall timeout expires, the delay task will throw a TaskCancelledException, which I convert into a TimeoutException before throwing it up the chain.


Here I’m basically waiting in a loop, periodically calling the signalFunc(), but able to fail when the cancellation token timer fires. Simple and elegant!


If you want the version that also has a cancellation token then that’s here for you too…

public static async Task<T> PollWithTimeoutAsync<T>(Func<bool> signalFunc, Func<T> dataFunc, int millisecondsBetweenPolls, int millisecondsToTimeout, CancellationToken cancellationToken)
{
if (null == signalFunc) throw new ArgumentNullException("signalFunc");
if (null == dataFunc) throw new ArgumentNullException("dataFunc");
if (millisecondsBetweenPolls >= millisecondsToTimeout) throw new ArgumentException("The millisecondsBetweenPolls should be less than millisecondsToTimeout");

using (var cts = new CancellationTokenSource(millisecondsToTimeout))
{
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken))
{
bool done = signalFunc();

while (!done)
{
try
{
await Task.Delay(millisecondsBetweenPolls, linkedCts.Token);
}
catch (TaskCanceledException)
{
// Was this a timeout?
if (cts.IsCancellationRequested)
throw new TimeoutException();
else
// No, it was most probably the outer cancellation token
throw;
}
done = signalFunc();
}

return dataFunc();
}
}
}



Hopefully someone will find this useful. If you want the code, complete with unit tests then please click here.

10 comments:

custom writing service reviews said...

I just have started my classes of .NET and i am going to bookmark your page. Because i am sure it will help me during my classes.

Oliver Maurice said...

I think that you should check here for some information about essay writing and essay hooks and how to deal with them. This could help you a lot

AngelaHolt said...

Hey!! Thanku you so much for sharing information about blog and also download spotify premium free android tinytunes latest version apk download gtunes music downloader apk

iphongthuy.net said...

iphongthuynet

ahmed5987 said...

تحتاج إلى الحفاظ على ساحة خالية من نفايات الحيوانات الأليفة للحفاظ على الآفات بعيدا. دائما التقاط النفايات الحيوانات الأليفة ووضعها في كيس من البلاستيك. عقدة الحقيبة ووضعها في سلة المهملات. لن يؤدي ذلك إلى إبقاء ساحة خالية من الآفات فحسب ، بل سيبقيها أيضًا أكثر نظافة وأمانًا لعائلتك.
شركة مكافحة حشرات بسكاكا
شركة رش مبيدات بسكاكا
المبيدات الحشرية الكيميائية
افضل شركة مكافحة حشرات

Sophie Grace said...

You completed certain reliable points there. I did a search on the subject and found nearly all persons will agree with your blog. WElcome visit our page: 192.168 l 254.254

Qasim Khan said...

This is the perfect post.assignment creators for students It helped me a lot. If you have time, I hope you come to my site and share your opinions. Have a nice day.

Ramma Foundation Repair said...

If your foundation is crumbling, it's time to call in the experts! Ramma Foundation is a foundation repair Edmonton company that has years of experience restoring foundations. We use the latest restoration methods and materials to get your home back to its former glory. Trust us to get your home back in order quickly and for a low price, so you can focus on more important things. Contact us today for a free consultation!

shopify said...

They have subject-specific school homework helpers who are ready to assist you all the time. pay someone to do your work

Angel17 said...

What an interesting and cool post. This is so useful! concrete foundation repair