Search code examples
c#datetimetimer

Is it possible that DateTime.UtcNow reports a wrong time after System.Threading.Timer elapses?


I have a piece of code that records the current time (DateTime.UtcNow) as a "deadline", sets up an System.Threading.Timer, and when the timer elapses checks if the deadline has been reached. It does this check by comparing the current time (DateTime.UtcNow) to the recorded deadline.

Here is a simplified code example that illustrates the principle:

class DeadlineChecker
{
  private DateTime deadline;

  public DeadlineChecker()
  {
    int waitingTimeInMilliseconds = 1200;

    TimeSpan waitingTime = TimeSpan.FromMilliseconds(waitingTimeInMilliseconds);
    this.deadline = DateTime.UtcNow + waitingTime;

    System.Threading.Timer timer = new System.Threading.Timer(TimerElapsed);
    timer.Change(waitingTimeInMilliseconds, Timeout.Infinite);
  }

  private void TimerElapsed(object state)
  {
    if (this.HasDeadlineBeenReached())
    {
      [...]  // Do something
    }

    [...] // Cleanup (dispose timer etc.)
  }

  private bool HasDeadlineBeenReached
  {
    get
    {
      // This is only called from TimerElapsed(), so
      // this should always return true, right?
      return (this.deadline <= DateTime.UtcNow);
    }
  }
}

I have now had a case where HasDeadlineBeenReached unexpectedly returns false. It appears that my assumption that HasDeadlineBeenReached always returns true in the example code above is wrong.

Can someone explain why? Before I refactor I would like to understand the issue.

Even taking the documented fact into account that DateTime.UtcNow has a 15ms resolution, I would not have expected that two snapshots of DateTime.UtcNow taken at times X and Y would report a time span that is smaller than what has actually elapsed (Y - X). Even if both snapshots are 15ms off, the time span between them should still be equal to the time that has elapsed due to the timer?

EDIT: What the real code tries to do is to set up a deadline, check that deadline when certain events occur, and "do something" when the deadline has been reached. The "timer elapsed" event is merely one of the many events that trigger the deadline check and is there to guarantee that "do something" happens in the absence of other events. It doesn't matter whether "do something" happens on time or a few milliseconds or even a second later than the deadline - there just needs to be a guarantee that it happens.

EDIT 2: I made a few minor changes to the code example after accepting the answer - bad style, I know, but maybe this makes the question a bit more intelligible to future generations.


Solution

  • You wrote:

    // This should always return true, right?
    

    No, it will only return true when deadline is in the past. I appreciate that you created a deadline that was 1200ms into the future and then told a timer to fire after 1200ms and are expecting the deadline to be in the past the instant the timer fires, but in reality the timing of timers and the accuracy of the clock is such that you might well reasonably observe your code not firing at the exact milliseconds you expect, and the clock not returning the exact milliseconds you expect, and as a result your deadline is still in the future when the code runs

    I suggest you adjust your timing strategy; set your timer for a half the interval you want to be "accurate" to interval, check your deadline to be in the past using the same logic, and tolerate the imprecision (don't promise the user that they can schedule the notification of their meeting with 1 millisecond precision)