Mishaa
Mishaa

Reputation: 97

CancellationToken in Web API not working correctly

I have Web Api which gets CancellationToken from users, the method inside it (DoWork) also get CancellationToken:

[HttpPost]
public async Task<long> GetInfo(CancellationToken cancellationToken)
{
   long result = 0;
   bool notDone = true;
   Task<long> t = Task.Run(async () =>
   {

      if (cancellationToken.IsCancellationRequested)
         cancellationToken.ThrowIfCancellationRequested();

      while (notDone && !cancellationToken.IsCancellationRequested)
      {
         result = await DoWork(cancellationToken);
         notDone = false;
      }

      return result;
   }, cancellationToken);
   try
   {
      return await t;
   }
   catch (AggregateException e)
   {
      Debug.WriteLine("Exception messages:");
      foreach (var ie in e.InnerExceptions)
         Debug.WriteLine("   {0}: {1}", ie.GetType().Name, ie.Message);

      Debug.WriteLine("\nTask status: {0}", t.Status);
      throw;
   }
   catch (Exception ex)
   {
      throw;
   }
}

private Task<long> DoWork(CancellationToken token)
{
   long result = 0;
   bool notDone = true;

   Task<long> task = Task.Run(() =>
   {

      if (token.IsCancellationRequested)
         token.ThrowIfCancellationRequested();

      while (notDone && !token.IsCancellationRequested)
      {
         Thread.Sleep(8000);
         result = 2;
         notDone = false;
      }

      return result;
   }, token);

   return task;
}

I expect when the user cancels the request it aborts the DoWork method and not continue the function, but after sending an Exception, when "Thread.Sleep" complete, the DoWork method continue. the user can call the API service like this method "cc" as you can see it cancel after 5 seconds and in the DoWork method "Thread.Sleep" is 9 seconds. the user gets an Exception but the method still running.

private async Task<bool> cc()
{
   UriBuilder builder = new UriBuilder("http://localhost:12458/api/Test/GetInfo");

   ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
   HttpClient client = new HttpClient();
   System.Threading.CancellationTokenSource s = new System.Threading.CancellationTokenSource();
   s.CancelAfter(5000);
   try
   {
      var result = client.PostAsJsonAsync<model1>(builder.ToString(), new model1 { }, s.Token).Result;
      string tmp = result.Content.ReadAsStringAsync().Result;
      long ApiResult = JsonConvert.DeserializeObject<long>(tmp);
   }
   catch (TaskCanceledException ex)
   {
   }
   catch (OperationCanceledException ex)
   {
   }
   catch (Exception ex)
   {
   }
   finally
   {
      s.Dispose();
   }
   return false;
}

Upvotes: 1

Views: 2024

Answers (3)

Rahul Jaiswal
Rahul Jaiswal

Reputation: 101

In my case it was because of fiddler. When I closed the fiddler app, it started working like a charm.

Upvotes: 0

Lana
Lana

Reputation: 1046

Ok, in your code you should process CancellationToken.IsCancellationRequested too. It's not kinda of magic, you should do this work.

    public void DoWork(CancellationToken ctsToken)
    {
        ctsToken.ThrowIfCancellationRequested();
        DoSomething();

        ctsToken.ThrowIfCancellationRequested();
        DoSomethingElse();

        // end so on with checking CancellationToken before every part of work
    }

And your Task should look like this

    Task<long> t = Task.Run(async () =>
    {
        cancellationToken.ThrowIfCancellationRequested();

        result = await DoWork(cancellationToken);
        notDone = false;

        cancellationToken.ThrowIfCancellationRequested();

        return result;
    }, cancellationToken);

Upvotes: 1

sa-es-ir
sa-es-ir

Reputation: 5042

When use Thread.Sleep(8000) actually hold the main thread for 8 seconds and It can't check the cancellation token. you should use Task.Delay with cancellation token like this:

while (notDone && !token.IsCancellationRequested)
  {
     await Task.Delay(8000, token);
     result = 2;
     notDone = false;
  }

Task.Delay check the cancellation token itself.

Upvotes: 4

Related Questions