Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Registered CancellationToken callback invocation order

Tags:

c#

async-await

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
like image 420
Arie Xiao Avatar asked Jan 12 '19 09:01

Arie Xiao


People also ask

What is difference between CancellationTokenSource and CancellationToken?

A CancellationTokenSource object, which provides a cancellation token through its Token property and sends a cancellation message by calling its Cancel or CancelAfter method. A CancellationToken object, which indicates whether cancellation is requested.

Does CancellationTokenSource need to be disposed?

Always call Dispose before you release your last reference to the CancellationTokenSource. Otherwise, the resources it is using will not be freed until the garbage collector calls the CancellationTokenSource object's Finalize method.

How can I check my cancellation token?

The wait handle of the cancellation token will become signaled in response to a cancellation request, and the method can use the return value of the WaitAny method to determine whether it was the cancellation token that signaled. The operation can then just exit, or throw an OperationCanceledException, as appropriate.

How does a CancellationToken work?

You can use a CancellationToken to stop a long running operation when the user cancels a request in the web browser. In other words, using a CancellationToken can help you stop long running requests from using resources when the user has stopped or refreshed the web page.


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"));
});
like image 186
Fabjan Avatar answered Nov 24 '22 07:11

Fabjan