Polly with cancellation token is not working for synchronous thread

436 views Asked by At

I'm new to Polly so there may be a completely different approach compared to the one I'm trying to do and that would be perfectly OK. My goals are this:

  1. I am using timeout strategy as: TimeoutStrategy.Optimistic
  2. I want to timeout the call for given clientTimeOut
  3. I am using cancellation token to timeout the thread

I am using cancellation token to timeout the call but somehow the below code is not working. I am not creating a new thread while adding a cancellation token but using the same thread to honoured the timeout. Does it always need to add the cancellation token in asynchronous call i.e. ExecuteAsync() or use another thread for it ? Can someone help me with what is wrong with the below code and why timeout is not working?

Note: Using TimeoutStrategy.Pessimist will work for me but I can’t use this due to some other usecase. I need to read the https headers from request context, and we will loose this information when using async call. Hence I need to use the sync call.

public TResult ExecuteWrapper<TResult>(Func<CancellationToken ,TResult> func)
{
    int attempts = 0;
    int retryCounter = 3;
    int waitTime = 1000;
    int clientTimeOut = 10000;
    var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(10000));
    var cancellationToken = cts.Token;

    RetryPolicy retryPolicy = Policy.Handle<Exception>().Retry(retryCounter);
    retryPolicy = Policy.Handle<Exception>().WaitAndRetry(retryCounter, sleepDurationProvider: attempt => TimeSpan.FromMilliseconds(this.waitTime), onRetry: (exception, timeSpan) =>
    {
          logRetryException(timeSpan, attempts++, exception);
    });
    
    PolicyWrap retrypolicywrap = retryPolicy.Wrap(Policy.Timeout(TimeSpan.FromMilliseconds(clientTimeOut), TimeoutStrategy.Optimistic));
    return retrypolicywrap.Execute(func, cancellationToken);
} 

public void BaseFunction(){
   var result = ExecuteWrapper(() => DummyFunctionToSleepTheThread())
}

public string DummyFunctionToSleepTheThread(){
   //Reading http header from the request context
   //which will lost if we use async call 
   //Hence we can't use Task.Delay(milliseconds) here.
    int milliseconds = 15000;
    System.Threading.Thread.Sleep(milliseconds);
    return "SUCCESS";
}

I tried adding the cancellation token but it did not honoured the timeout.

1

There are 1 answers

0
Peter Csala On

You can't use optimistic timeout strategy to cancel a Thread. This strategy uses the co-operative behaviour of the CancellationToken. Threads do not natively support CancellationToken like Tasks.

So, you have two options. Fall back to pessimistic timeout strategy or use Task instead of Thread.

As far as I can tell from the your code you haven't designed your solution to take advantage of Tasks and async-await. So, try to use the pessimistic strategy.


UPDATE #1

Here is a simple app which demonstrates the difference

(Please note that bellow overload of Timeout requires seconds not milliseconds)

public static void Main()
{
    var optimisticTimeout = Policy.Timeout(1, TimeoutStrategy.Optimistic);
    optimisticTimeout.Execute(ToBeDecoratedMethod);
    var pessimisticTimeout = Policy.Timeout(1, TimeoutStrategy.Pessimistic);
    try
    {
        pessimisticTimeout.Execute(() => ToBeDecoratedMethod());
    }
    catch(TimeoutRejectedException)
    {
        Console.WriteLine("Timed out");
    }
}

public static void ToBeDecoratedMethod()
{
    Console.WriteLine("Before Sleep");
    Thread.Sleep(1_500);
    Console.WriteLine("After Sleep");
}

The output will be:

Before Sleep
After Sleep
Before Sleep
Timed out

The working code can be found on this dotnet fiddle link.