Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the most stable time stamp Source for a .NET application?

Tags:

c#

datetime

Base Issue

I am having an issue with time stamps in my C# application. I receive data from a remote TCP connection asynchronously. Every time I receive data, I update a time stamp variable to DateTime.Now. On a separate thread, once a second, I check if it has been longer than a predefined timeout period since my last receive and if so, disconnect. This method has been working for many years, but now I have a situation where the application is installed on a machine with an unstable time source. Every few days, the machine time "auto-corrects" and I drop the connection prematurely. The code is basically as follows:

Receive Process

void OnReceiveComplete(IAsyncResult ar) {
    ...
    mySocket.EndReceive(ar);
    lastRxTime = DateTime.Now;
    ...
}

Check Process

void CheckConnection() {
    TimeSpan ts = DateTime.Now.Subtract(lastRxTime);
    if(ts.TotalMilliseconds > timeout) {
        Disconnect(string.Format("No packet received from the server for over {0} seconds.", timeout / 1000));
    }
}

I have valid Wireshark captures during the issue and right before the disconnect I see NTP traffic that culminates in what looks like a correction of at least 1 minute. This obviously causes the check process to fail.

Technical Details/Answers to Expected Questions

  1. I have control of both ends of the connection, but not the physical layer in between (often a low quality satellite link), This is why this timeout check is in place.
  2. Since the data is Asynchronous, there is a provision to send out a small heartbeat if no data has been sent from the server for a period of time equal to half the timeout.
  3. There can be multiple instances of this process (i.e. on machine connecting to multiple different servers.
  4. All communications use Asynchronous methods (which supposedly use completion ports).
  5. The check process is run on a separate thread that is shared by all clients on a single machine.

Completed Research (i.e. Possible Solutions)

My Google searches to this point have lead to the following:

  1. I realize I should have used DateTime.UtcNow instead of DateTime.Now for performance reasons. This would not affect the issue itself.
  2. An implementation depending on Ticks would be a better solution.
  3. There are two options for getting ticks - Environment.TickCount and Stopwatch.GetTimestamp()
  4. According to my research, Environment.TickCount may be susceptible to time adjustments, but I am not sure under what circumstances. Also, since I use this same methodology in other higher performance circumstances, the 10-16 mSec resolution may be an issue (though not in the specific case I am presenting here).
  5. The Stopwatch.GetTimestamp() can fallback to DateTime.Now.Ticks when a High Performance clock is unavailable. I am unsure how often that will occur (do any machines NOT come with a high performance clock anymore), but I am certain that if it resorts to Ticks the same issue will occur.
  6. I have also read that Stopwatch.GetTimestamp() will use the QueryPerformanceCounter() API call, and that can be unstable when called from multiple threads.

Ultimate Question

I am curious what the best method to generate the lastRxTime time stamp would be? Am I worrying too much about low likelihood issues in the Environment.TickCount and Stopwatch.GetTimestamp() functions? I am open to alternative implementations as long as they take the multi-threaded nature of the application as well as the link quality issues into account.

UPDATE 7/17/2013 (A solution has been deployed!)

I have deployed a solution and want to let everyone in on the details. In general, there may not be one accepted answer, but after going through this experience I can say that the original solution was definately an issue. I will try to provide as much detail as possible:

First, the NTP issue was actually a symptom of a different issue. The network exhibiting the issue is AD Domain with the two servers running my code set up as Domain Controllers. It turns out DCs are time sources for the domain. It also turns out that the system time drifts from the real time clock on these systems by up to a minute over approximately 11 days, at which point Windows is correcting the slip. Once it corrects the slip on the first DC, the second DC syncs his time and both run into the problem described above.

Based on the feedback and my original research, I created a test program to run during a disconnect to log values for DateTime.Now, Environment.TickCount, and Stopwatch.GetTimestamp(). What I found was that during the correction, neither Environment.TickCount nor StopWatch.GetTimeStamp() slipped at all, meaning they were good candidates to use as a replacement for DateTime.Now(). I went with TickCount because it is guaranteed to be on all of my deployed servers (whereas the stopwatch could fall back to the DateTime object on some machines that I have yet to find). It is working so far without issue. I did due dilligence on the roll-over issue to prevent that form being a problem, but will need to wait for my system to be up that long be sure.

I want to note that if anybody else is experiencing similar problems, they should not discount the use of any of the other presented solutions in the list below. Each has its own merrit, in fact the simple counter is likely the best solution for most circumstances. The reason I did not go to this solution was that I have similar code in separate area that depends heavily on tight timings. I cna handle the 16 mSec or so resolution of the tick count there, but cannot handle the drift of time that the counter solutions incur (I used code like that in a separate product that wound up drifting by over a second an hour and bringing me out of spec for the project).

Again, thanks to all and if any more comes up I will be sure to update the question.

like image 804
Heilgates Avatar asked Jun 12 '13 23:06

Heilgates


3 Answers

I don't see any problem with using Environment.TickCount. The documentation states that it returns:

A 32-bit signed integer containing the amount of time in milliseconds that has passed since the last time the computer was started.

It goes on to say:

The value of this property is derived from the system timer and is stored as a 32-bit signed integer.

I'm not seeing how NTP time correction can affect this. Do you have a link that suggests otherwise? The tricky part is dealing with the wrap-around. So, when comparing the current tick count to the previous, if you get a negative number, you'll know wrap-around occurred.

Personally, I think this is the superior mechanism to use, because the return value is in millisonds, so the conversion factor for your timeout will be straightforward. Otherwise, with Stopwatch.GetTimestamp you'll have to do some additional work to factor in the tick frequency.

Furthermore, I'm assuming this is implemented internally with a call to the Win32 GetTickCount function. (dotPeek shows that it's marked with MethodImplOptions.InternalCall, so I can't tell for sure) But the documentation for that states:

Remarks

The resolution of the GetTickCount function is limited to the resolution of the system timer, which is typically in the range of 10 milliseconds to 16 milliseconds. The resolution of the GetTickCount function is not affected by adjustments made by the GetSystemTimeAdjustment function.


On second thought, since you're periodically checking only every second, the resolution of the timer only has to be < 1 sec. There is zero point in trying to do any better, so why use any API call at all?

private int m_conTimer;

void OneSecondThreadCallback() {
    if (++m_conTimer >= TIMEOUT_VALUE)
        // Connection timed out. React accordingly.
}
like image 153
Jonathon Reinhart Avatar answered Dec 15 '22 06:12

Jonathon Reinhart


There are a few good reasons not to use DateTime.Now for this purpose

  • As you pointed out, there are performance issues. Mostly this is because it internally has to translate to the local time zone.

  • Since it is the "now" of the local time zone, it is unsuitable for comparison or calculation. This is because many time zones have discontinuities twice a year as Daylight Saving Time transitions occur. If you are comparing against your last event time, then when the clocks roll forward you would have an extra hour. When they roll back, you could be off anywhere from 0 to 60 minutes (depending on exactly when you did your comparison).

  • It has a much lower precision than Stopwatch or Environment.TickCount. While DateTime is capable of representing very small amounts of time, the system clock itself is only accurate to about 10 to 30 ms. (But perhaps that's not so important for your use case.)

Using DateTime.UtcNow does address the first two points, but not the third. For that, you need to stay away from DateTime completely.

Also - there is the concept of "clock drift". Clocks may slow down or speed up over extended periods. If the OS has a time synchronization source, it can actually adjust the amount of time that actually passes in a tick in order to compensate. You can read more in this article (see the "Time Adjustments" section).

You may also be interested in the ClockRes utility can be used to give you information about your system timer resolution.

HOWEVER - your use case is probably best served not by any of these techniques. You said you were interested in cancelling an event after a timeout period. You also said that you have a separate thread that checks periodically to see if the timeout period has elapsed. All of that is unnecssary. Instead, just use a Timer class.

Be careful, there are three different timers available in .Net. You probably want a System.Threading.Timer:

// start the timer, callback in 10000 milliseconds, and don't fire more than once
var timer = new Timer(TimerCallback, null, 10000, Timeout.Infinite);

// to reset the timer when you receive data
timer.Change(10000, Timeout.Infinite);

// to stop the timer completely
timer.Change(Timeout.Infinite, Timeout.Infinite);

// and in your callback
private void TimerCallback(object state)
{
    // disconnect, or do whatever you want
}

If its possible you would be resetting the timer from multiple threads, then you should instead use a System.Timers.Timer - which is thread safe.

// to set up the timer
var timer = new Timer(10000) {AutoReset = false};
timer.Elapsed += TimerOnElapsed;

// to start the timer running
timer.Start();

// to reset the timer
timer.Stop();
timer.Start();

// and the callback
private void TimerOnElapsed(object sender, ElapsedEventArgs args)
{
    // disconnect, or do whatever you want
}
like image 43
Matt Johnson-Pint Avatar answered Dec 15 '22 05:12

Matt Johnson-Pint


Am I worrying too much about low likelihood issues in the Environment.TickCount and Stopwatch.GetTimestamp() functions?

Potentially, yes.

I would prefer the Stopwatch.GetTimestamp() option. This will use high performance timers if they are available on your system, and fallback to DateTime.Ticks if that isn't an option. As such, you'll get the best possible timing when available on your given hardware, and a good option as a fallback when high performance timers are not available.

While Environment.TickCount could be an interesting option, you would need to handle the case where this overflows if it's possible that your machine will be operational for more than 24.9 days.

like image 42
Reed Copsey Avatar answered Dec 15 '22 06:12

Reed Copsey