Using System.Threading.Timer in Compact Framework

compact-frameworktimer

I am using a System.Threading.Timer in a CF project (Windows Embedded CE 6.0), VS2005 C#, .NET 2.0.
This timer is desired because there is no possibility of reentrancy when used like this:

    private System.Threading.Timer mainTimer;

    private void MainForm_Load(object sender, EventArgs e)
    {
        // other initializations
        mainTimer = new System.Threading.Timer(new TimerCallback(timerMain_Tick),
                null, 100, Timeout.Infinite);
    }

Which is to say, dueTime parameter is used but period is not. As long as period is Timeout.Infinite, the timer will fire once only. The timer is made thread-safe by checking for the form's InvokeRequired property. Note the check for null. It relates to my question, which I am getting to quickly.

    private void timerMain_Tick(object stateInfo)
    {
        if (mainTimer != null)
        {
            if (this.InvokeRequired)
            {
                this.Invoke((ThreadStart)delegate
                {
                    TimerProcess();
                });
            }
            else
            {
                TimerProcess();
            }
        }
    }

The timer must restart itself before it exits.

    private void TimerProcess()
    {
        try
        {
                // do work here
        }
        finally
        {
            // retrigger
            mainTimer.Change(mainTimerInterval, Timeout.Infinite);
        }
    }

The problem I am having is gracefully stopping this darn thing.

    private void MainForm_Closing(object sender, CancelEventArgs e)
    {
        // shut down timer
        mainTimer.Change(Timeout.Infinite, Timeout.Infinite);
        mainTimer.Dispose();
        mainTimer = null;
    }

About 3 times in 10, the timer fires anyway, and I get an Object Disposed error. The timer code is trying to invoke the timer method AFTER the check for null.
I suspect that the timer fires, and its thread is suspended while the form is closing. I tried a state machine enumeration:

Normal state Running

Form_Closing sets Stopping state and waits in a Thread.Sleep() loop for Stopped state

Timer sees Stopping and sets Stopped state (rather than retriggering itself)

Problem I had with this is that the timer thread would not preempt the form closing method, so get stuck in endless loop.
How to fix this problem? Note that in CF, there is no Dispose(WaitHandle) method.

Best Solution

Interesting problem. There do not seem to be many options with the Timer in the Compact Framework.

I'm not sure how your specific code works, so adding a single static Boolean value may or may not fix your issues.

Here is how I changed your code to accept a timerOK value. If this does not solve your problem, it could give you ideas on how to approach this.

private static bool timerOK;
private static long mainTimerInterval = 200;
private System.Threading.Timer mainTimer;

private void MainForm_Load(object sender, EventArgs e) {
  timerOK = true;
  mainTimer = new System.Threading.Timer(new TimerCallback(timerMain_Tick), null, 100, Timeout.Infinite);
}

private void MainForm_Closing(object sender, CancelEventArgs e) {
  timerOK = false;
  mainTimer.Change(Timeout.Infinite, Timeout.Infinite);
  mainTimer.Dispose();
  mainTimer = null;
}

private void timerMain_Tick(object stateInfo) {
  if (timerOK && (mainTimer != null)) {
    if (this.InvokeRequired) {
      this.Invoke((ThreadStart)delegate {
        TimerProcess();
      });
    } else {
      TimerProcess();
    }
  }
}

private void TimerProcess() {
  if (!timerOK) return;
  try {
    // do work here
  } finally {
    // retrigger
    mainTimer.Change(mainTimerInterval, Timeout.Infinite);
  }
}
Related Question