Search code examples
c#timer

NullReferenceException on Timer.Change


In our production code we have a random exception 'System.NullReferenceException' that comes every now and then. after some searching we see it coming from a 'DoWork' function that is a callback of a 'System.Threading.Timer'.

I have reduced the code to a minimum to reproduce the problem. It seems to happen when for 'dueTime' a 0 is passed, and in the 'DoWork' on the function '_timer.Change' the exception is thrown. below is sample code, if you press enter a couple of times, you should get the error (sometimes faster than other times). the locking around disposing is there for test, but it also happens without that.

Something that i noticed was that if the doWork does not contain a lot of work, so is almost instantly at the _timer.Change, it seems to happen more frequently

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace TimerFault
{
class Program
{
    static void Main(string[] args)
    {
        List<TimerTest2> timers = new List<TimerTest2>();

        while (true)
        {
            for (int count = 0; count < 100; count++)
            {
                timers.Add(new TimerTest2());
            }
            Console.ReadLine();

            timers.ForEach(x => x.Dispose());

            timers.Clear();
        }
    }

    public class TimerTest2 : IDisposable
    {
        private readonly System.Threading.Timer _timer;
        public TimerTest2()
        {
            _timer = new System.Threading.Timer(DoWork, null, 0, Timeout.Infinite);
        }
        bool isDisposed = false;
        readonly object locker = new object();

        public void Dispose()
        {
            lock (locker)
            {
                isDisposed = true;
                _timer.Dispose();
            }
        }

        void DoWork(object state)
        {
            lock (locker)
            {

                if (!isDisposed)
                    _timer.Change(100, Timeout.Infinite);
            }
        }
    }
}
}

The full exception details are folowing:

System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=TimerFault
  StackTrace:
   at TimerFault.Program.TimerTest2.DoWork(Object state) in D:\Visual 
Studio\Projects\TimerFault\TimerFault\Program.cs:line 52
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext 
executionContext, ContextCallback callback, Object state, Boolean 
preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext 
executionContext, ContextCallback callback, Object state, Boolean 
preserveSyncCtx)
   at System.Threading.TimerQueueTimer.CallCallback()
   at System.Threading.TimerQueueTimer.Fire()
   at System.Threading.TimerQueue.FireNextTimers()

Am i doing something wrong? i cant seem to get it. Again: The code is reduced, this is not the actual production code.


Solution

  • I believe i have found the answer, it it quite logical if i think about it now, but it was counter intuitive for me.

    If the dueTime of the timer is set to 0, it will fire the doWork function as soon as possible, even if the constructor of the timer is not yet completed, ad the _timer field is not yet set to the instance of the timer. So the doWork function gets called, and tries to call the Change function on the timer before the _timer field of the class was set, and therefore gets a null reference exception. when debugging the _timer was never null, but it was probably set right after the exception happend, and before the program was set to break state.

    if i change the constructor to the folowing the exception does not get thrown anymore:

    public TimerTest2()
    {
        _timer = new Timer(DoWork);
        _timer.Change(0, Timeout.Infinite);
    }