I have a code which depends on a cancellation token with zero timeout to bail out early. This is the snippet
using System;
using System.Threading;
namespace ConsoleApp2
{
class Program
{
void DoIdleWait(TimeSpan timeout, CancellationToken cancellationToken)
{
var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
linkedCancellationTokenSource.CancelAfter(timeout);
while (!linkedCancellationTokenSource.IsCancellationRequested)
{
Console.WriteLine("Waiting");
}
}
static void Main(string[] args)
{
var prog = new Program();
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(2));
prog.DoIdleWait(TimeSpan.Zero, cts.Token);
}
}
}
Since the timeout is zero, I expect it not to enter the if block, but it's not doing that. Any idea why this is happening? Also, any way to achieve what I am trying to do?
CodePudding user response:
To paraphrase the question,
Why, when you create a linked
CancellationTokenSource
from aCancellationTokenSource
with a registered timeout, then set the resultant token source with a timeout of zero, does it not know it should be cancelled?
More paraphrasing
After all, zero is zero and it should know its cancelled...
The answer is, because a CancellationTokenSource.CancelAfter
tries to register a timer callback, which attempts to set the resolution to zero milliseconds which it cannot possibly honor..
So in turn, you get the minimum resolution any standard timing mechanism can give you without using CPU spins, which is about 5 milliseconds...
There are likely other solutions to your problem, however, in short you cant rely on a 0
second timeout to give you immediate confirmation via IsCancellationRequested
. You will need to rethink your problem.
CodePudding user response:
The CancellationTokenSource.CancelAfter
method (source code) does not include a special handling for the TimeSpan.Zero
value, so the cancellation is scheduled to the ThreadPool
with a Timer
having zero timeout. This results to a race condition, which the synchronous while (!linkedCancellationTokenSource.IsCancellationRequested)
loop wins most of the time. The only solution I can think of, taking into account that the implementation of the DoIdleWait
method is not under your control, is to not invoke this method if you know that the timeout is zero.