6

Microsoft gives this example of CancellationToken use in .NET 4.

using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
    static void Main()
    {

        var tokenSource2 = new CancellationTokenSource();
        CancellationToken ct = tokenSource2.Token;

        var task = Task.Factory.StartNew(() =>
        {

            // Were we already canceled?
            ct.ThrowIfCancellationRequested();

            bool moreToDo = true;
            while (moreToDo)
            {
                // Poll on this property if you have to do
                // other cleanup before throwing.
                if (ct.IsCancellationRequested)
                {
                    // Clean up here, then...
                    ct.ThrowIfCancellationRequested();
                }

            }
        }, tokenSource2.Token); // Pass same token to StartNew.

        tokenSource2.Cancel();

        // Just continue on this thread, or Wait/WaitAll with try-catch:
        try
        {
            task.Wait();
        }
        catch (AggregateException e)
        {
            foreach (var v in e.InnerExceptions)
                Console.WriteLine(e.Message + " " + v.Message);
        }

        Console.ReadKey();
    }
}

However, my understanding is that if a variable is modified on one thread, another thread might not get the modified value, due to caching. And since the CancellationToken is cancelled on the main thread, how is the Task thread assured that the CancellationToken it is checking is actually up-to-date?

Why isn't it possible for the Task to be reading a cached value of the token?

Note: My motivation in asking this arises from wondering whether I need my CancellationToken instance variables to be volatile.

Community
  • 1
  • 1
Eric
  • 5,842
  • 7
  • 42
  • 71

1 Answers1

12

This is handled internally within CancellationTokenSource. The private variable used to track the CTS's state is marked volatile, which prevents the internal state check from being stale.

My motivation in asking this arises from wondering whether I need my CancellationToken instance variables to be volatile.

You do not need to do this, as the checking is handled internally, and already handled properly for you.

Basically, when you create a CancellationToken from the CancellationTokenSource, the token contains a reference to the original source. This reference can never change, so the call to ThrowIfCancellationRequested checks the state of the source internally. Since the source state is itself volatile, it's never "stale" data.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 2
    Plus, CancellationTokenSource is a reference type. Using volatile on a CancellationTokenSource variable only makes changing/reading the reference to that field volatile--which doesn't occur when the TPL does anything with the CancellationTokenSource object. Rest-assure, the CancellationTokenSource internals are thread-safe, as Reed describes. – Peter Ritchie Aug 07 '12 at 16:31
  • @PeterRitchie I think the OP was wondering whether he needed to declare his `CancellationToken` (which is a struct) as volatile, not the CTS... But yeah, since the token is immutable, and holds the reference to the CTS, it "just works" – Reed Copsey Aug 07 '12 at 16:32
  • Hmm, I must have gotten confused with the "volatile" since `volatile` can't be used on a struct and therefore a `CancellationToken` field could never be decorated with "volatile". Even if `CancellationToken` weren't immutable, any changes to a value type can only be seen on that copy--although a copy of `ct` should never be made in the posted code. – Peter Ritchie Aug 07 '12 at 16:49
  • @PeterRitchie Yeah - it was a misunderstanding on the OP's part, I believe - it wouldn't have worked, but that was what was asked in the question – Reed Copsey Aug 07 '12 at 16:53