Go to content Go to navigation Go to search

Brokenwire.NET::Programming

Timer.Elapsed event – The Silent Killer
· 2009-02-26 12:54 by Thijs Kroesbergen for Brokenwire.NET

Yesterday we ran into some issue where exceptions would disappear into a black hole. We had some code running within the Elapsed event of a System.Timers.Timer which seemed to crash without us being able to catch the exception in our global “unhandled exception” exception handler. It looked like we had a Ninja in our code somewhere, deadly and accurate.

So we finally figured out what was happening and I’ll explain it to you here.

To start with the code to reproduce this looks like this:
An example project can be downloaded here: SilentKiller.zip

static void Main(string[] args)
{
    Console.WriteLine("Starting...");
    AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
    System.Timers.Timer aTimer;
    aTimer = new Timer(1000);
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
    aTimer.Enabled = true;
    Console.WriteLine("Press the Enter key to exit the program.");
    Console.ReadLine();
}

private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    Console.WriteLine("Ninja attack!");
    throw new ApplicationException("Ninja!");
    Console.WriteLine("You didn't see this one coming");
}

private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    Exception ex = (Exception)e.ExceptionObject;
    Console.WriteLine("Caught an exception: " + ex.Message);
}

The problem here is that you’d expect the OnUnhandledException method to fire, but this doesn’t happen. It seems like the exception is silenced within the timer! It took us quite a while to find that out. But actually the MSDN documentation is accurate about this, and my colleague Paul was the first to catch this line:

In the .NET Framework version 2.0 and earlier, the Timer component catches and suppresses all exceptions thrown by event handlers for the Elapsed event. This behavior is subject to change in future releases of the .NET Framework.

So it’s not a bug, it’s a feature!

And when you peek inside the System.dll assembly from the .Net framework you’ll see the following code for the handling of the Elapsed event

private void MyTimerCallback(object state)
{
    if (state == this.cookie)
    {
        if (!this.autoReset)
        {
            this.enabled = false;
        }
        FILE_TIME lpSystemTimeAsFileTime = new FILE_TIME();
        GetSystemTimeAsFileTime(ref lpSystemTimeAsFileTime);
        ElapsedEventArgs e = new ElapsedEventArgs(lpSystemTimeAsFileTime.ftTimeLow, lpSystemTimeAsFileTime.ftTimeHigh);
        try
        {
            ElapsedEventHandler onIntervalElapsed = this.onIntervalElapsed;
            if (onIntervalElapsed != null)
            {
                if ((this.SynchronizingObject != null) && this.SynchronizingObject.InvokeRequired)
                {
                    this.SynchronizingObject.BeginInvoke(onIntervalElapsed, new object[] { this, e });
                }
                else
                {
                    onIntervalElapsed(this, e);
                }
            }
        }
        catch
        {
        }
    }
}

Now I wonder who had the guts to break the rule “Never (ever) use an empty catch”! And why did they do this?!

(This would be a nice question for a Microsoft exam ;). How many of you knew about this?)

Permalink -