After my application froze I tracked down the cause to a thread waiting on a task created by Task.Delay()
(or TaskEx.Delay()
in .NET 4.0) for which it provided a computed TimeSpan
that, due to a bug, was on occasion computed to a TimeSpan
with a TotalMilliseconds
of less than or equal to -1
and greater than -2
(i.e. anywhere between -10000 to -19999 ticks, inclusive).
It appears that when you pass a negative TimeSpan
that is -2
milliseconds or lower, the method correctly throws an ArgumentOutOfRangeException
, but when you provide a negative TimeSpan from the range described above, it returns a Task
that never completes (by setting the underlying System.Threading.Timer
to a dueTime
of -1 which denotes infinity). That means that any continuations set on that task will never execute, and any poor thread that happens to .Wait()
on that Task
will forever be blocked.
What possible use can a Task
that never completes have? Would anyone expect such a return value? Shouldn't any negative value passed to .Delay()
, including values in that special range, throw an ArgumentOutOfRangeException
?
Timeout.Infinite or -1 is useful when you want to wait indefinitely for a long-running task that will take an indeterminate amount of time to complete, but will eventually complete.
The Win32 API also uses a constant INFINITE = -1 for infinite timeouts.
You wouldn't normally want to use it in a UI thread, as it could freeze the UI (which seems to be your problem). But there are valid use cases in a worker thread - e.g. a server that is blocking waiting for a connection from a client.
In mocking scenarios, where I want to ensure that my code in a Task.WhenAny() block is handling one of the tasks awaited on correctly, I may mock the other tasks and use an infinite delay to ensure that the Task.WhenAny is processing the task I did not mock as an infinite delay.
I'm using it when I want console application not to terminate.
For example, when I'm writing a Web Job for Auzre, I need a program that never terminates. Here's the template:
public async Task Main(string args[])
{
var timer = new Timer();
timer.Interval = 6000;
timer.Elapsed += (sender, args) =>
{
// Do something repeatedly, each 1 minute.
};
timer.Start();
// Now what to do to stop the application from terminating!?
// That's the magic:
await Task.Delay(-1);
}