System.Timers.Timer and System.Threading.Timer bug after 49.7 days!
Hello, we have noticed what appears to be a bug in the Timer classes mentioned in the subject.
These classes can stop working correctly after 49.7 days.From what we've observed in our Production environment, and also the available information on the net, I am confident this is related to the issue whereby the number of milliseconds since reboot (which the timer classes use) wraps from 2^32 (about 49.7 days) back to zero.
It seems that what's happening internally is that when we set a timer, .NET or the OS is adding the timer interval to the current count of milliseconds since reboot. If this happens to pass the 2^32 threshold, it wraps back to start from zero, so the target time is incorrectly calculated to be BEFORE the current time. The timer fires immediately.
Our operating environment is Windows Server 2003, .NET 2.0 in 32-bit mode, all current service packs installed as of Feb 2008.
We have a number of Windows Services and ASP.NET applications, and they are all affected by the issue.
I've seen a handful of threads at other sites referring to this same behaviour, which makes me think I'm not crazy.
What I've not found, however, is acknowledgement from MS that this is a bug, and the official fix/workaround.
My questions are:
- Is this a bug? (These classes would fail unit testing in spectacular fashion if left for 50 days)
- Is there is an official fix or work-around from MS?
thanks,
Clayton
Todas las respuestas
A small clarification... our OS is Windows Server 2003 64-bit. We are currently running most of our apps in 32-bit mode for legacy reasons.
I'm guessing this issue probably does not exist when running in full 64-bit mode, but for us to make this move would require a fair amount of testing and co-ordination. Also, we have some ASP.NET 1.1 applications, which have to be run in 32-bit mode, and I believe you cannot have IIS hosting both 32-bit and 64-bit applications simultaneously on the same box.
This aside, I'm still seeking clarification on the status of the issue when running .NET in 32-bit mode so that we can assess our options.
thanks!
Hello,
I'm curious to know what type of timers you're using.
I know that there are 3 types types of timers :
- this coming from Windows.Forms for which the interval is in Int32 so you will have some trouble after 50 days
- this coming From System.Timers for which the interval is in Double so you will have some trouble after so many days that i think that your prog would have been thrown into the dustbin before.
- the third as i don't remember it so i will not be speaking about it ( thread timer ?? )
Have a look on :
http://msdn2.microsoft.com/en-us/library/system.timers.aspx
http://msdn2.microsoft.com/en-us/library/system.timers.timer.interval.aspx
If you want to use System.Timers, i want to point you a problem
Interval is adouble so you "might" ( the documentation is not clear ) to use an interval = 1,000,000,000,000 milliseconds
but if you use
System.Timers.Timer timer = new System.Timers.Timer(1,000,000,000,000) ==> bug because of ArgumentException
The value of the interval parameter is less than or equal to zero, or greater than Int32..::.MaxValue
Surprising...
If you have some trouble with overflow in System.Int32, i suggest you to get the starting date o the computer throu WMI.
I'm currently using and it's easy
Unfortunately, the code is in another computer ( unavailable due to backing up ).
I will post it when i will be able to access this computer
Have a nice day
I am referring to System.Threading.Timer and System.Timers.Timer. The latter uses the former for its implementation, which is why they both suffer from the bug.
We are not using Windows Forms timers.
As you state later in your post, although the "Interval" property of the System.Timers.Timer class is a double, you cannot actually assign values greater than Int32.MaxValue.
We don't need to set particularly large interval values... only a single day at most.
I am not sure what you are suggesting regarding retrieval of the starting date of the computer through WMI...?
I want to find out how to get the timer class(es) to work reliably.
Although we know that these classes internally use the time since reboot, that is an implementation detail that should not be my problem.
thanks
Set the timer interval to the DateTime you want the timer to fire minus Now. When the timer fires, check to ensure that the time is greater than or equal to the desired time. If it is do whatever you need to do. If not set the timer again.
Hello,
Not only a single day at most : 11 days for 1,000,000,000 milliseconds
Test that :
using
System;using
System.Collections.Generic;using
System.ComponentModel;using
System.Data;using
System.Drawing;using
System.Text;using
System.Timers;using
System.Windows.Forms;namespace
Test_Timer{
public partial class Form1 : Form{
public static Double duration; public static System.Timers.Timer timer; public Form1(){
InitializeComponent();
duration = 1000000000;
TimeSpan ts = new TimeSpan(0,0,0,0,(int)duration); String str = ts.Days.ToString() + " days " + ts.Hours.ToString() + " hours ";str += ts.Minutes.ToString() +
" minutes";DurationTxt.Text = str;
timer =
new System.Timers.Timer();timer.Interval = duration;
timer.Elapsed +=
new ElapsedEventHandler(timer_Elapsed);timer.Enabled =
true;timer.Start();
}
private static void timer_Elapsed(object source,ElapsedEventArgs e){
MessageBox.Show(duration.ToString() + " milliseconds");}
private void Form1_FormClosing(object sender, FormClosingEventArgs e){
MessageBox.Show("Program is closed");}
}
}
You have only 2 controls :
a label with duration as text
a textbox named DurationTxt where i display a TimeSpan equal to 1,000,000,000 milliseconds
The problem is on the level of documentation of timer :
when you look on :http://msdn2.microsoft.com/en-us/library/system.timers.timer.start.aspx
you will see ArgumentOutOfRangeException
The Timer is created with an interval equal to or greater than Int32..::.MaxValue + 1, or set to an interval less than zero
but for Interval http://msdn2.microsoft.com/en-us/library/system.timers.timer.interval.aspx
you have ArgumentException : The time, in milliseconds, between raisings of the Elapsed event. The default is 100 milliseconds with no indication of upper limit.
I think that Microsoft has defined interval as a Double but with using this property as a Int32.
For System.Threading.Timer, the documentation is clearer with a limit to 4,294,967,294 = 47 days...
I don't think there is a simple way to pass these limits.
So i think you can't use a timer to do what you want ( except with a counter of times you will restart your timer )
Well, could you have a look on
http://msdn2.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
Maybe it's your solution but i don't think
Have a nice day
Remarks : i will post in the MSDN Documentation forum to ask if it's possible to have a clearer view of the use of interval
John, thanks for your suggested workaround.
Unfortunately this will still give us some pretty undesirable behaviour.
It will lead to a tight loop, and consume 100% of CPU, until the required interval no longer causes the wrap-around. For us, where we want our timer to fire once a day, that can last up to a day in the worst case.
If I have to implement a work-around that still uses the built in timer class, it will more lightly require checking in advance for wrap-around, and setting a smaller interval, to minimise erroneously triggered events as much as possible. The callback would be a custom/internal one defined by the wrapper class, and could do the checking you suggest.
I understand there are some options to work-around this, but I'd still like confirmation that it's a bug

If running a unit test on these classes for 50 days produces a failure, in my book they fail.
thanks!The timers have been like this since Windows 95. My guess is that they don't want to break legacy applications.
Where is this tight loop. The only difference from what I assume you had before was that you check when your timer ticks that the time is correct. The timer will fire in error once every 50 days. The time is incorrect, so you set a new interval which will tick at the correct time.
The timer definately does not fire in error once every 50 days.
My apologies, I guess my description of the issue wasn't very clear.
Once the timer fires erroneously, due to the required interval causing a wrap-around in the time-since-reboot counter, it will CONTINUE to fire erroneously until the required interval no longer causes a wrap-around.
Also note that when this occurs, creating and using a different timer object, or indeed restarting the application altogether, will not fix the issue, because the wrap-around is completely independant of that.
As far as I'm aware, the only ways to stop the timer from firing erroneously are:
a) Set a smaller interval, to avoid wrap-around (but then you obviously don't get the interval you want, so you have to reset the interval again, checking until the correct time has elapsed), OR
b) Reboot
If you keep trying to set the interval you want, it will continue to fire erroneously until that interval no longer causes wrap-around, leading to the tight-loop.
This is precisely the behaviour we are seeing in our environment (including Production), very consistently and predictably.
Checking that the correct amount of time has elapsed will ensure that we don't run the task early, but it won't ensure that you don't consume all the CPU.
I've no doubt the 32-bit counter of milliseconds since reboot probably has been around since Windows 95, but I'm fairly confident that .NET 2.0 was not around then!
This is a bug in the these classes in the .NET Framework.
If there are underlying OS constraints that make life difficult for the timer classes, the designers of the classes should have included logic to work-around these, so that the classes perform their function.
Failing that, it should be made clear in the documentation that these classes don't always work.
Yeh. Everytime you set the timer by check the date you have to set a different interval. If you don't want to do that use GetTickCount to calculate the intervals. Since there is a GetTickCount64 in Vista, the behaviour may have changed in Vista. Otherwise, complain to Microsoft and wait till they change it to your liking.
Yep, I'm thinking that going to 64-bit is probably the most practical and robust solution (assuming it does resolve the issue!)
I guess I was hoping people from MS monitor these forums, and might provide the "official" position.
I'm more than a little surprised that there have not been enough complaints regarding this to warrant a fix from MS.
Until now I had assumed that the .NET timers were quite reliable, and presumably got a lot of use in a lot of applications, including large enterprise apps.
I believe I was quite mistaken!
This is another one of those days I wish I was a Java man

- try read it, maybe the hotfix can resolve your problem.
http://support.microsoft.com/?scid=kb%3Ben-us%3B944762&x=18&y=13

