1

It seems like right after I call my first async method (GetBar() in this example), the CancellationToken's IsCancellationRequested is set to true, but I don't want that and don't understand why it's happening.

This is in an Azure Cloud Service worker role, if that matters.

public class WorkerRole : RoleEntryPoint
{
    private CancellationTokenSource cancellationTokenSource;
    private Task runTask;

    public override void Run()
    {
        this.cancellationTokenSource = new CancellationTokenSource();
        this.runTask = Task.Run(() => Foo.Bar(this.cancellationTokenSource.Token), this.cancellationTokenSource.Token);
    }

    public override void OnStop()
    {
        this.cancellationTokenSource.Cancel();

        try
        {
            this.runTask.Wait();
        }
        catch (Exception e)
        {
            Logger.Error(e, e.Message);
        }

        base.OnStop();
    }

    // ... OnStart omitted
}

public static class Foo
{
    public static async Bar(CancellationToken token)
    {
        while (true)
        {
            try
            {
                token.ThrowIfCancellationRequested();

                var bar = await FooService.GetBar().ConfigureAwait(false);

                // Now token.IsCancellationRequested == true. Why? The above call does not take the token as input.
            }
            catch (OperationCanceledException)
            {
                // ... Handling
            }
        }
    }
}

I've successfully used CancellationTokens once before in another project and I use a similar setup here. The only difference I'm aware of is that this is in an Azure Cloud Service. Any idea why IsCancellationRequested is getting set to true?

MattM
  • 1,159
  • 1
  • 13
  • 25
  • 1
    What other code touches `cancellationTokenSource` besides the two lines you showed in `Run()`, also can `Run()` be called multiple times? If I had to guess I would say you have a race condition with the `this.cancellationTokenSource.Token` inside the lambada and that is causing the token getting passed in to `Foo.Bar(` to be not the same token that was passed in as the 2nd parameter of `Task.Run(` – Scott Chamberlain Oct 19 '16 at 18:47
  • 1
    Are you getting an exception thrown for `GetBar`? Check this out, and see if it helps: http://stackoverflow.com/questions/13489065/best-practice-to-call-configureawait-for-all-server-side-code – Mike_G Oct 19 '16 at 18:48
  • 1
    Per your update, could `OnStop` have been called while you where awaiting for `FooService.GetBar()` to complete? Perhaps add some form of logging to see if `OnStop` is called between before the `token.ThrowIfCancellationRequested();` and after the `var bar = await ...` – Scott Chamberlain Oct 19 '16 at 18:50
  • @ScottChamberlain Yeah, looks like `OnStop` is getting called, you're totally right. That's weird...I wouldn't have expected that. Thanks to both of you for your fast responses. Edit: Feel free to post your last comment as an answer and I'll accept it. Now to troubleshoot why `OnStop` is being called :) – MattM Oct 19 '16 at 18:53

1 Answers1

1

It appears OnStop was called while you where awaiting for FooService.GetBar() to complete. Perhaps add some form of logging to see if OnStop is called between the token.ThrowIfCancellationRequested(); and after the var bar = await ... returns to confirm.

That is what is causing the token to be canceled.

To solve the problem you need to make sure the overridden Run method does not return till the work is complete.

public override void Run()
{
    this.cancellationTokenSource = new CancellationTokenSource();
    this.runTask = Task.Run(() => Foo.Bar(this.cancellationTokenSource.Token), this.cancellationTokenSource.Token);
    this.runTask.Wait(); //You may need a try/catch around it
}
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Just to clarify, the reason `OnStop` is getting called is because my `Run` method ends up returning. Found that info here: https://azure.microsoft.com/en-us/documentation/articles/cloud-services-role-lifecycle-dotnet/#run-method – MattM Oct 19 '16 at 19:10
  • Makes sense. Updated my answer to include the info. – Scott Chamberlain Oct 19 '16 at 19:25