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:
void OnReceiveComplete(IAsyncResult ar) {
...
mySocket.EndReceive(ar);
lastRxTime = DateTime.Now;
...
}
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.
My Google searches to this point have lead to the following:
DateTime.UtcNow
instead of DateTime.Now
for performance reasons. This would not affect the issue itself.Environment.TickCount
and Stopwatch.GetTimestamp()
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).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.Stopwatch.GetTimestamp()
will use the QueryPerformanceCounter()
API call, and that can be unstable when called from multiple threads.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.
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.
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 theGetSystemTimeAdjustment
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.
}
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
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With