How to enable timeout policy with polly - c#

I'm trying out the timeout policy with polly.
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
timeoutPolicy().GetAwaiter().GetResult();
stopwatch.Stop();
Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
Console.ReadKey();
}
static async Task timeoutPolicy()
{
AsyncTimeoutPolicy<HttpResponseMessage> timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(1); // setup the timeout limit to be 1 sec
HttpResponseMessage response = await timeoutPolicy.ExecuteAsync((ct) => LongOperation(), CancellationToken.None);
}
static Task<HttpResponseMessage> LongOperation()
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
{
Thread.Sleep(5000); // Sleep 5 seconds
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest
};
});
}
I expect an exception to be thrown after 1 sec passed because that is the timeout upper limit I set up. But currently, no exception will be thrown and the method LongOperation() returns normally after around 5 secs.
Why does the timeout policy not work in this case?

Why does the timeout policy not work in this case?
Polly's TimeoutPolicy exists in two modes:
TimeoutStrategy.Optimistic expects governed delegates to respond to co-operative cancellation by CancellationToken.
TimeoutStrategy.Pessimistic allows the calling code to walk away from waiting for a delegate that doesn't respond to co-operative cancellation.
Optimistic mode is the default, so your posted code uses this. But (a) LongOperation() in the posted code does not respond to co-operative cancellation; so the policy does not time it out.
Pessimistic mode with asynchronous policies is intentionally designed only to govern delegates which conform to the normal async pattern. Thread.Sleep() in the posted LongOperation() is fully sychronous; so your example would additionally not be timed out just by switching to TimeoutStrategy.Pessimistic.
TimeoutStrategy.Optimistic is the best simulation of calls through HttpClient, as those calls do respond to CancellationToken.
Async timeout policy's optimistic mode timing out a long operation can be simulated with await Task.Delay(...) honouring a CancellationToken, like this:
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Polly;
using Polly.Timeout;
public class Program
{
public static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
try {
timeoutPolicy().GetAwaiter().GetResult();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
stopwatch.Stop();
Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
}
static async Task timeoutPolicy()
{
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(1); // setup the timeout limit to be 1 sec
HttpResponseMessage response = await timeoutPolicy.ExecuteAsync((ct) => LongOperation(ct), CancellationToken.None);
}
static async Task<HttpResponseMessage> LongOperation(CancellationToken token)
{
await Task.Delay(5000, token);
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest
};
}
}

Right now you're using Optimistic timeout which expects the delgate you're calling to respect the and respond to the cancellation token. Your delegate does not, in this case you need to use Pessimistic timeout to ensure your caller doesn't wait passed the defined timeout.
static async Task timeoutPolicy()
{
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(1, TimeoutStrategy.Pessimistic); // setup the timeout limit to be 1 sec
HttpResponseMessage response = await timeoutPolicy.ExecuteAsync((ct) => LongOperation(), CancellationToken.None);
}
Polly Timeout as stated in the docs Optimistic is the default.
As #moutain traveller already pointed out, optimistic timeout requires you to to pass and co-operate with the timeout cancellation. This is handled already by api calls using the HttpClient but in this sample case your code would look something like this:
static async Task timeoutPolicy()
{
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(1, TimeoutStrategy.Optimistic); // setup the timeout limit to be 1 sec
HttpResponseMessage response = await timeoutPolicy.ExecuteAsync((ct) => LongOperation(ct), CancellationToken.None);
}
static Task<HttpResponseMessage> LongOperation(CancellationToken token)
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
{
var longTask = Task.Delay(5000);
while (!longTask.IsCompleted)
{
token.ThrowIfCancellationRequested();
}
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest
};
});
}

Related

Serial port awaiter - cancel if device is not responding for 5 secs | when flag doesnt change

I'm trying to make a app that comunicates with a device through serial port.
I'm lacking the idea to "stop" the program when there is no response for some time.
I got the idea to make a flag that I'm setting to false while sending data, in my DataReceiveEvent I'm changing the flag to true. But at the same time I want a timer that if no response is registered will fire an event.
I thought of using async Tasks.
var delay = Task.Run(async () => { await Task.Delay(3000); return false;});
var flag = Task.Run(async () => { await isFlagChanged(); return isFlagChanged});
var result = await Task.WhenAny(delay, flag);
bool res = await result;
but I don't know how to determine if flag had changed. I don't even know if that's a good idea...
Any sugestions?
Have your Async Task reset a CancellationTokenSource timeout each time it receives information.
If it doesn't receive any data then the CancelationToken will fire and your Task can exit. Your function that will be reading the serial port, needs to be checking the CancellationToken.
eg:,
static async Task MyTaskAsync(CancellationTokenSource cts)
{
await Task.Yield();
CancellationToken ct = cts.Token;
var rnd = new Random();
while (true)
{
var next = rnd.Next(10);
Console.WriteLine(next);
if (next == 1)
{
await Task.Delay(500, ct); // Simulate receiving no data by waiting longer the timeout
ct.ThrowIfCancellationRequested();
}
else
{
await Task.Delay(100, ct); // Simulate receiving data by waiting less than the timeout
cts.CancelAfter(500);
}
ct.ThrowIfCancellationRequested();
}
}
static async Task Main(string[] args)
{
var cts = new CancellationTokenSource(500); // Auto cancel after 500ms
try
{
await MyTaskAsync(cts);
}
catch (OperationCanceledException)
{
Console.WriteLine("The task exited because the Cancellation timeout period expired.");
}
}

Timeout and stop a Task

I have a console app,
{
StartThread();
//must be true, windows system wants to know it is started
return true;
}
I'm trying to create a safety timeout function for this Task. But the task keeps running...
The method DoSomething calls other async methods and awaits their result
Do anyone have an idea why my task don't stop? Maybe a good code example on what to do
public async void StartThread()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
try
{
var timeout = 1000;
Task task = new Task(() => DoSomething(token), token);
task.Start();
if (await Task.WhenAny(task, Task.Delay(timeout, token)) == task)
{
if (token.IsCancellationRequested)
task.Dispose();
await task;
if (task.IsCompleted)
{
task.Dispose();
tokenSource.Cancel();
tokenSource.Dispose();
}
else
{
log.WriteToFile("Timeout_ ");
}
}
else
tokenSource.Cancel();
}
catch (Exception e)
{
Console.WriteLine("--StartThread ...there is an exception----");
}
finally
{
Thread.Sleep(300000); // 5 minutter
StartThread();
}
}
While not create CancellationTokenSource from given timeout?
var timeout = 1000;
//DONE: don't forget to dispose CancellationTokenSource instance
using (var tokenSource = new CancellationTokenSource(timeout)) {
try {
var token = tokenSource.Token;
//TODO: May be you'll want to add .ConfigureAwait(false);
Task task = Task.Run(() => DoSomething(token), token);
await task;
// Completed
}
catch (TaskCanceledException) {
// Cancelled due to timeout
log.WriteToFile("Timeout_ ");
}
catch (Exception e) {
// Failed to complete due to e exception
Console.WriteLine("--StartThread ...there is an exception----");
//DONE: let's be nice and don't swallow the exception
throw;
}
}
You should hardly ever Dispose a Task, since iot is managed by C# internals and it is taken care of. Also, you Dispose way too eagerly, for example:
if (token.IsCancellationRequested)
task.Dispose();
await task;
I do not think so you want still await task if it cancelled and disposed. I guess it will not work at all.
Also if you use async, do not mix blocking calls such as Thread.Sleep - that can lead to disaster...
After all, you use cancellation token with some delay task to imitate a timeout - that's OK, but why do put unnecessary code, when you have great API at hand. Just make use of special contructor of CancellationTokenSource:
public CancellationTokenSource (int millisecondsDelay);
Here's the docs
After a timeout you are setting the CancellationToken and then immediately sleeping the thread for 5 minutes. Thus DoSomething() never gets a chance to continue running and react to the token being cancelled.

Time out reversal

I need to integrate an API into my development with a specific scenario called "Time Out Reversal" (TOR)
This means, succintly, to comply with the following requirements:
Initiate a request by invoking an endpoint
If a response is not received within a defined time out
start a reversal request by invoking another endpoint
While the requirements seems very clear to me, I really haven't found a way to implement it by using tasks.
For example, I know how to delay a task but I dont really know how to set a timeout in seconds for a started task
Any suggestion?
You can easily implement a timeout like so:
public async Task TimoutReversal() {
var timeout = TimeSpan.FromSeconds(10);
try {
//assumes HttpClient
Client.Timeout = timeout;
await Client.GetAsync(firstEndpoint);
} catch (OperationCanceledException ex) {
await Client.DeleteAsync(secondEndpoint);
}
}
//or
public async Task TimoutReversal() {
var timeout = TimeSpan.FromSeconds(10);
var firstTask = Client.GetAsync(firstEndpoint);
if (await Task.WhenAny(firstTask, Task.Delay(timeout)) == firstTask) {
//success
}else {
//failure
await Client.DeleteAsync(secondEndpoint);
}
}
Also see protected answer here on SO: Task with timeout
Another option is to pass the Token from a CancellationTokenSource created from a TimeSpan to the HttpClient calls.

What happens when HttpClient usages are not awaited

Given code similar to
Task.Run(() =>
{
using (var client = new HttpClient())
{
var responseTask = client.GetAsync(urlToInvoke);
}
});
In a situation like this, it appears that GetAsync does not actually operate. Is the task canceled prior to completion or what is actually going on here?
Now if you change things slightly and insert
Task.Run(() =>
{
using (var client = new HttpClient())
{
var responseTask = client.GetAsync(urlToInvoke);
Task.Delay(5000).Wait()
}
});
GetAsync does execute completely. What is going on here? Is Task.Delay affinitizing itself to the same Task that is inside responseTask ultimately making this equivalent to responseTask.Wait()?
You are thinking of it incorrectly. Here is pseudo version of what is happening inside the class.
class HttpClient : IDisposeable
{
private CancelationTokenSource _disposeCts;
public HttpClient()
{
_disposeCts = new CancelationTokenSource();
}
public Task<HttpResponseMessage> GetAsync(string url)
{
return GetAsync(url, CancellationToken.None);
}
public async Task<HttpResponseMessage> GetAsync(string url, CancelationToken token)
{
var combinedCts =
CancellationTokenSource.CreateLinkedTokenSource(token, _disposeCts.Token);
var tokenToUse = combinedCts.Token;
//... snipped code
//Some spot where it would good to check if we have canceled yet.
tokenToUse.ThrowIfCancellationRequested();
//... More snipped code;
return result;
}
public void Dispose()
{
_disposeCts.Cancel();
}
//... A whole bunch of other stuff.
}
The important thing to see is when you exit the using block a internal cancelation token is canceled.
In your first example the task had not finished yet so tokenToUse would now throw if ThrowIfCancellationRequested() was called.
In your second example the task had already finished so the act of canceling the internal token had no effect on the task that was returned due to it already reaching the completed state.
It is like asking why this causes the task to be canceled.
using (var client = new HttpClient())
{
var cts = new CancellationTokenSource()
var responseTask = client.GetAsync(urlToInvoke, cts.Token);
cts.Cancel();
}
but this does not
using (var client = new HttpClient())
{
var cts = new CancellationTokenSource()
var responseTask = client.GetAsync(urlToInvoke, cts.Token);
Task.Delay(5000).Wait()
cts.Cancel();
}
When you don't await (or Wait) tasks they do not cancel themselves. They continue to run until they reach one of three statuses:
RanToCompletion - Finished successfully.
Canceled - The cancellation token was canceled.
Faulted - An unhandled exception occurred inside the task.
In your case however, because no one waits for the task to complete, the using scope ends which disposes of the HttpClient. This in turn will cancel all the client's tasks, client.GetAsync(urlToInvoke) in this case. So that inner async task will end immediately and become Canceled, while the outer task (Task.Run) will simply end without doing anything.
When you use Task.Delay(5000).Wait() which is basically Thread.Sleep(5000) the task has a chance to complete before the using scope ends. That mode of operation however should be avoided. It blocks a thread throughout the Waitand could lead to deadlocks in single threaded SynchronizationContexts. This also hides possible exceptions in the task (which could tear down the application in earlier versions of .Net)
You should always wait for tasks to complete, preferably asynchronously, and as Servy commented, there's no reason to use Task.Run here for offloading because GetAsyncis asynchronous and won't block the calling thread.
using (var client = new HttpClient())
{
var response = await client.GetAsync(urlToInvoke);
}

Monitoring a synchronous method for timeout

I'm looking for an efficient way to throw a timeout exception if a synchronous method takes too long to execute. I've seen some samples but nothing that quite does what I want.
What I need to do is
Check that the sync method does exceed its SLA
If it does throw a timeout exception
I do not have to terminate the sync method if it executes for too long. (Multiple failures will trip a circuit breaker and prevent cascading failure)
My solution so far is show below. Note that I do pass a CancellationToken to the sync method in the hope that it will honor a cancellation request on timeout. Also my solution returns a task that can then be awaited on etc as desired by my calling code.
My concern is that this code creates two tasks per method being monitoring. I think the TPL will manage this well, but I would like to confirm.
Does this make sense? Is there a better way to do this?
private Task TimeoutSyncMethod( Action<CancellationToken> syncAction, TimeSpan timeout )
{
var cts = new CancellationTokenSource();
var outer = Task.Run( () =>
{
try
{
//Start the synchronous method - passing it a cancellation token
var inner = Task.Run( () => syncAction( cts.Token ), cts.Token );
if( !inner.Wait( timeout ) )
{
//Try give the sync method a chance to abort grecefully
cts.Cancel();
//There was a timeout regardless of what the sync method does - so throw
throw new TimeoutException( "Timeout waiting for method after " + timeout );
}
}
finally
{
cts.Dispose();
}
}, cts.Token );
return outer;
}
Edit:
Using #Timothy's answer I'm now using this. While not significantly less code it is a lot clearer. Thanks!
private Task TimeoutSyncMethod( Action<CancellationToken> syncAction, TimeSpan timeout )
{
var cts = new CancellationTokenSource();
var inner = Task.Run( () => syncAction( cts.Token ), cts.Token );
var delay = Task.Delay( timeout, cts.Token );
var timeoutTask = Task.WhenAny( inner, delay ).ContinueWith( t =>
{
try
{
if( !inner.IsCompleted )
{
cts.Cancel();
throw new TimeoutException( "Timeout waiting for method after " + timeout );
}
}
finally
{
cts.Dispose();
}
}, cts.Token );
return timeoutTask;
}
If you have a Task called task, you can do this:
var delay = Task.Delay(TimeSpan.FromSeconds(3));
var timeoutTask = Task.WhenAny(task, delay);
If timeoutTask.Result ends up being task, then it didn't timeout. Otherwise, it's delay and it did timeout.
I don't know if this is going to behave identically to what you have implemented, but it's the built-in way to do this.
I have re-written this solution for .NET 4.0 where some methods are not available e.g.Delay. This version is monitoring a method which returns object. How to implement Delay in .NET 4.0 comes from here: How to put a task to sleep (or delay) in C# 4.0?
public class OperationWithTimeout
{
public Task<object> Execute(Func<CancellationToken, object> operation, TimeSpan timeout)
{
var cancellationToken = new CancellationTokenSource();
// Two tasks are created.
// One which starts the requested operation and second which starts Timer.
// Timer is set to AutoReset = false so it runs only once after given 'delayTime'.
// When this 'delayTime' has elapsed then TaskCompletionSource.TrySetResult() method is executed.
// This method attempts to transition the 'delayTask' into the RanToCompletion state.
Task<object> operationTask = Task<object>.Factory.StartNew(() => operation(cancellationToken.Token), cancellationToken.Token);
Task delayTask = Delay(timeout.TotalMilliseconds);
// Then WaitAny() waits for any of the provided task objects to complete execution.
Task[] tasks = new Task[]{operationTask, delayTask};
Task.WaitAny(tasks);
try
{
if (!operationTask.IsCompleted)
{
// If operation task didn't finish within given timeout call Cancel() on token and throw 'TimeoutException' exception.
// If Cancel() was called then in the operation itself the property 'IsCancellationRequested' will be equal to 'true'.
cancellationToken.Cancel();
throw new TimeoutException("Timeout waiting for method after " + timeout + ". Method was to slow :-)");
}
}
finally
{
cancellationToken.Dispose();
}
return operationTask;
}
public static Task Delay(double delayTime)
{
var completionSource = new TaskCompletionSource<bool>();
Timer timer = new Timer();
timer.Elapsed += (obj, args) => completionSource.TrySetResult(true);
timer.Interval = delayTime;
timer.AutoReset = false;
timer.Start();
return completionSource.Task;
}
}
How to use it then in Console app.
public static void Main(string[] args)
{
var operationWithTimeout = new OperationWithTimeout();
TimeSpan timeout = TimeSpan.FromMilliseconds(10000);
Func<CancellationToken, object> operation = token =>
{
Thread.Sleep(9000); // 12000
if (token.IsCancellationRequested)
{
Console.Write("Operation was cancelled.");
return null;
}
return 123456;
};
try
{
var t = operationWithTimeout.Execute(operation, timeout);
var result = t.Result;
Console.WriteLine("Operation returned '" + result + "'");
}
catch (TimeoutException tex)
{
Console.WriteLine(tex.Message);
}
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
To elabolate on Timothy Shields clean solution:
if (task == await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(3))))
{
return await task;
}
else
throw new TimeoutException();
This solution, I found, will also handle the case where the Task has a return value - i.e:
async Task<T>
More to be found here: MSDN: Crafting a Task.TimeoutAfter Method
Jasper's answer got me most of the way, but I specifically wanted a void function to call a non-task synchronous method with a timeout. Here's what I ended up with:
public static void RunWithTimeout(Action action, TimeSpan timeout)
{
var task = Task.Run(action);
try
{
var success = task.Wait(timeout);
if (!success)
{
throw new TimeoutException();
}
}
catch (AggregateException ex)
{
throw ex.InnerException;
}
}
Call it like:
RunWithTimeout(() => File.Copy(..), TimeSpan.FromSeconds(3));

Categories

Resources