1

I have a asynchronous process that is taking too much time to return results, so I want to set the cancellation token to 3 minutes.

So, I have the cancellation token cancelling at a set amount of time, but the await base.SendAsync stays in the await state waiting for a response, how do I break out of that await state as soon as the the timer on the cancellation token is up?

using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Net;

namespace MyModel.Models
{
    public class MyHandler : DelegatingHandler
    {
        protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
        {

        var response = new HttpResponseMessage();
        bool isCancelled = false;

        using (var cts = new CancellationTokenSource(System.TimeSpan.FromSeconds(15))) //just an example, so I will change to 3 minutes in future.
        {
        response = await base.SendAsync(request, cts.Token).ConfigureAwait(false);

           if(cts.Token.IsCancellationRequested) isCancelled = true;
        }

            if ((response.StatusCode != HttpStatusCode.NotFound) && (!isCancelled))
            {
                      //do work       
            }
            return response;
        }
    }
}
ConfusedDeer
  • 3,335
  • 8
  • 44
  • 72
  • https://stackoverflow.com/questions/35615638/any-way-to-differentiate-cancel-and-timeout I think the answer here can help you, I don't believe it's a duplicate – rogerdeuce Jun 13 '16 at 18:10
  • So, How do I know when the async process has been cancelled? My current problem is that I set the cancellation token to 3 minutes, but the async process keeps running. – ConfusedDeer Jun 13 '16 at 19:45
  • looks like you are looking to catch an `ObjectDisposedException`. This is not anything I've played with myself so I'm not gonna be much help. Check out this documentation https://msdn.microsoft.com/en-us/library/hh194893 – rogerdeuce Jun 13 '16 at 19:52

3 Answers3

0

Use this:

new System.Threading.CancellationTokenSource(System.TimeSpan.FromMinutes(3.0)).Token

ConfusedDeer
  • 3,335
  • 8
  • 44
  • 72
Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59
0

While I agree with the previous answer, I would add something:

using(var cancelTokenSource = new System.Threading.CancellationTokenSource(System.TimeSpan.FromMinutes(3.0)))
{
...
}

Since System.Threading.CancellationTokenSource implements IDisposable

Greg
  • 69
  • 4
0

HttpClient has a Timeout property and I think the behavior varies based on which underlying Handler is being used (HttpClientHandler or WebRequestHandler typically). I believe with HttpClientHandler, you can use a CancellationTokenSource with a timer to short circuit that timeout alarm (trigger cancellation prior to the timeout), but if the request times out before your Cancellation token times out, your request will cancel before your 3 minute mark.

For example if your caller looked like this:

public async Task InvokeRequest()
{
    using (var cts = new CancellationTokenSource(System.TimeSpan.FromMinutes(3)))
    {
         await myHandler.SendAsync(new HttpRequestMessage { ... }, cts.Token).ConfigureAwait(false);
    }        
}

...but the HttpClient.Timeout was 100 seconds (I think the default), the request would cancel at 100 seconds, not 180.

See this question for more details: Whats the difference between HttpClient.Timeout and using the WebRequestHandler timeout properties?

Community
  • 1
  • 1
scottt732
  • 3,877
  • 2
  • 21
  • 23
  • What does ConfigureAwaitFalse(); do? I'm getting a Task does not contain a definition for 'ConfigureAwaitFalse'... – ConfusedDeer Jun 15 '16 at 15:15
  • In regards to the previous comment this is my code: var response = new HttpResponseMessage(); using (var cts = new CancellationTokenSource(System.TimeSpan.FromMinutes(3))) { response = await base.SendAsync(request, cts.Token).ConfigureAwaitFalse(); } – ConfusedDeer Jun 15 '16 at 15:16
  • Sorry. Corrected. It should read `ConfigureAwait(false);`. You should put it after any async call in library-level code. It prevents the framework from capturing & restoring a synchronization context before/after the awaited expression. Library-level code doesn't need it because presumably the caller will capture & restore it if they need it. It's a perf optimization and can prevent deadlocks in certain situations. – scottt732 Jun 15 '16 at 15:17
  • So what happens when I cancel, will I get a response? Once the request is cancelled is there a cancellation boolean that I can check to redirect the user to a proper error page? – ConfusedDeer Jun 15 '16 at 15:22
  • Try/Catch an `OperationCanceledException` at the caller. Note that as a general rule of thumb, GET requests should have no side effects but PUT, POST, DELETE, etc., can. It's just a convention & there are tons of API's out there that don't respect it. Cancelling a request doesn't necessarily imply that the server you're talking to hasn't already/isn't going to do something based on your request. Many web servers/apps won't even notice that the client isn't waiting on a response anymore. – scottt732 Jun 15 '16 at 15:25
  • So, I have the cancellation token cancelling at a set amount of time, but the await base.SendAsync stays in the await state waiting for a response, how do I break out of that await state as soon as the the timer on the cancellation token is up? – ConfusedDeer Jun 15 '16 at 15:55
  • 1
    If the HttpClient's timeout or the CancellationTokenSource's timer expire before the request completes, your `await base.SendAsync(...)` will rejoin & immediately throw an OperationCancelledException. Typically API's that accept a cancellation token call cancellationToken.ThrowIfCancellationRequested() periodically/before any expensive calls (e.g., I/O). That method just checks if IsCancelled = true and throws an OperationCancelledException if it is. The CancellationTokenSource's timer will effectively mark IsCancelled true, asking the consumer of the token to throw the OperationCanceledEx – scottt732 Jun 15 '16 at 16:17