Linking Cancellation Tokens

Retrocoder picture Retrocoder · Apr 14, 2015 · Viewed 14.7k times · Source

I use a cancellation token that is passed around so that my service can be shut down cleanly. The service has logic that keeps trying to connect to other services, so the token is a good way to break out of these retry loops running in separate threads. My problem is that I need to make a call to a service which has internal retry logic but to return after a set period if a retry fails. I would like to create a new cancellation token with a timeout which will do this for me. The problem with this is that my new token isn't linked to the “master” token so when the master token is cancelled, my new token will still be alive until it times-out or the connection is made and it returns. What I would like to do is link the two tokens together so that when the master one is cancelled my new one will also cancel. I tried using the CancellationTokenSource.CreateLinkedTokenSource method but when my new token timed-out, it also cancelled the master token. Is there a way to do what I need to do with tokens or will it require changes to the retry logic (probably not going to be able to do this easily)

Here is what I want to do:

Master Token – passed around various functions so that the service can shut down cleanly. Temporary Token – passed to a single function and set to timeout after one minute

If the Master Token is cancelled, the Temporary Token must also be cancelled.

When the Temporary Token expires it must NOT cancel the Master Token.

Answer

i3arnon picture i3arnon · Apr 14, 2015

You want to use CancellationTokenSource.CreateLinkedTokenSource. It allows to have a "parent" and a "child" CancellationTokenSourcees. Here's a simple example:

var parentCts = new CancellationTokenSource();
var childCts = CancellationTokenSource.CreateLinkedTokenSource(parentCts.Token);

childCts.CancelAfter(1000);
Console.WriteLine("Cancel child CTS");
Thread.Sleep(2000);
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);
Console.WriteLine();

parentCts.Cancel();
Console.WriteLine("Cancel parent CTS");
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);

Output as expected:

Cancel child CTS
Child CTS: True
Parent CTS: False

Cancel parent CTS
Child CTS: True
Parent CTS: True