locked
System.Timers.Timer and System.Threading.Timer bug after 49.7 days! RRS feed

  • Question

  • 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

     

     

    Monday, March 3, 2008 10:00 PM

All replies

  • 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!

    Monday, March 3, 2008 10:20 PM
  • 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

     

    Tuesday, March 4, 2008 6:42 AM
  • 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

    Tuesday, March 4, 2008 10:16 AM
  • 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.

    Tuesday, March 4, 2008 10:33 AM
  • 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

    Tuesday, March 4, 2008 1:43 PM
  • 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 Smile

     

    If running a unit test on these classes for 50 days produces a failure, in my book they fail.

     

    thanks!
    Tuesday, March 4, 2008 9:18 PM
  • 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. 

    Tuesday, March 4, 2008 9:44 PM
  • 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.

    Wednesday, March 5, 2008 12:09 AM
  • 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.

    Wednesday, March 5, 2008 4:50 AM
  • 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  Sad

    Wednesday, March 5, 2008 5:22 AM
  • try read it, maybe the hotfix can resolve your problem.

    http://support.microsoft.com/?scid=kb%3Ben-us%3B944762&x=18&y=13
    Friday, March 7, 2008 2:59 PM
  • Hi  claytona_123

    If you are having this problem why can't try something like this...

    You are creating the timer with the specific interval and it starts to fire the event incorrectly after 49.7 days (I guess this your problem in short, If I'm wrong please state)

    In your timerElapsed event handler you can put your code what has to be done and in the end dispose the timer object and create a new timer with the specified time period. And add the current event handler.

    I didn't try it in my code but I think it'll work. because every time the timer fires the event we r creating a new timer object..

    :) 

    Thursday, March 11, 2010 5:51 AM
  • Hi there.

    This is a real issue after 49.7days. I did not have experience regarding that, But after reading the posts I talked to a person  (who is a DPE) in Microsoft Srilanka. 

    The issue is there in 32 bit Windows 2003 and it is because the kernel time is synced with according to the day light saving and the server time is set to the current locale's time zone

    He told they simply restart the services to avoid this problem. 
    Friday, March 12, 2010 4:12 PM
  • I just stumbled onto this old thread.  Timers don't provide a reliable time source, never have.  Use the timer to check some reliable time source.

     

    Public Class Form1
    
      'using windows forms timer for an EXAMPLE
    
      Dim whenToRun As DateTime
      Dim ts As New TimeSpan(0, 0, 0, 0, 1000000000)
    
      Private Sub Form1_Load(ByVal sender As System.Object, _
                  ByVal e As System.EventArgs) Handles MyBase.Load
    
        whenToRun = DateTime.Now + ts
    
        'this should be set, IMHO, to half the error you are willing to accept
        Timer1.Interval = 30000 'check every half minute
        Timer1.Start()
    
      End Sub
    
      Private Sub Timer1_Tick(ByVal sender As System.Object, _
                  ByVal e As System.EventArgs) Handles Timer1.Tick
    
        'are we within the window?
        If DateTime.Now.AddMilliseconds(Timer1.Interval) >= whenToRun Then
          'yes, do your stuff
          whenToRun = DateTime.Now + ts 'reset for next interval
        End If
    
      End Sub
    End Class
    


    Subnet Calculator / Planner      Serial Port
    Sunday, July 4, 2010 4:16 PM
  • I agree with dbasnett,

     

    That timers never provide a proper solution on timing based event handlers. Stick to some atomic timer code to make it accurate.

    However the kernel has to handle everything


    Regards Puboo My blog : http://thurupathan.spaces.live.com
    Wednesday, July 7, 2010 9:58 AM