Registered CancellationToken callback invocation order

Viewed 261

If I register some callbacks to a CancellationToken before it is cancelled, it seems they will be invoked in reverse order when the token is cancelled. Is this invocation order guaranteed?

var cts = new CancellationTokenSource();
var token = cts.Token;
token.Register(() => Console.WriteLine("1"));
token.Register(() => Console.WriteLine("2"));
token.Register(() => Console.WriteLine("3"));
cts.Cancel();

This will output

3
2
1
1 Answers

Well, from source code of class CancellationToken it appears to be so. The method ExecuteCallbackHandlers(bool throwOnFirstException) is responsible for getting callbacks and executing them. It contains this fragment of code:

try
{
   for (int index = 0; index < callbackLists.Length; index++)
   {
      SparselyPopulatedArray<CancellationCallbackInfo> list = Volatile.Read<SparselyPopulatedArray<CancellationCallbackInfo>>(ref callbackLists[index]);
      if (list != null)
      {
         SparselyPopulatedArrayFragment<CancellationCallbackInfo> currArrayFragment = list.Tail;

         while (currArrayFragment != null)
         {
             for (int i = currArrayFragment.Length - 1; i >= 0; i--)
             {
                ... some other code
             }
        }
     }
   }
}

In the inner for loop it traverses the fragment of array of callbacks backwards from last to first element.

However as noted by @Nick this is not guaranteed in the documentation. A simple solution would be to add callbacks into one delegate - this way we have more control over the execution order:

token.Register(() => 
{ 
   Console.WriteLine("1"));
   Console.WriteLine("2"));
   Console.WriteLine("3"));
});
Related