3

I am working on a Windows Service that essentially runs the following code:

public class ServiceController
{
  private FundsEngine _fundsEngine;

  protected override void OnStart(string[] args)
  {
    var logsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
    if (Directory.Exists(logsPath) == false)
    {
      Directory.CreateDirectory(logsPath);
    }

    _fundsEngine = new FundsEngine();
    _fundsEngine.Start();
  }
}

The service can start up "Engines" which are just classes that use Timers to execute code on a specified interval.

The following class is an example of an Engine that uses System.Threading.Timer to invoke a callback, OnTick(), every 5 seconds.

public class FundsEngine
{
  private System.Threading.Timer _timer;
  private static Logger _logger = LogManager.GetCurrentClassLogger();

  public void Start()
  {
    _logger.Trace("Begin Start");

    try
    {
      // details omitted

      _timer = new Timer(OnTick);
      _timer.Change(5000, Timeout.Infinite);
    }
    catch (Exception exception)
    {
      _logger.ErrorException("Error in Start.", exception);
    }
    finally
    {
      _logger.Trace("End Start");
    }
  }

  private void OnTick(object state)
  {
    _logger.Trace("Begin Tick");

    try
    {
      // do stuff
      _timer.Change(5000, Timeout.Infinite);
    }
    catch (Exception exception)
    {
      _logger.ErrorException("Error in Tick. ", exception);
    }
    finally
    {
      _logger.Trace("End Tick");
    }
  }
}

As shown in the log below, the Timer works as expected, invoking the OnTick() method every 5 seconds.

2013-05-14 16:27:01.2261|TRACE|Begin Start 
2013-05-14 16:27:03.8514|TRACE|End Start 
2013-05-14 16:27:08.8569|TRACE|Begin Tick 
2013-05-14 16:27:08.8709|TRACE|End Tick 
2013-05-14 16:27:13.8734|TRACE|Begin Tick 
2013-05-14 16:27:13.8734|TRACE|End Tick 
2013-05-14 16:27:18.8809|TRACE|Begin Tick 
2013-05-14 16:27:18.8809|TRACE|End Tick 
2013-05-14 16:27:23.8894|TRACE|Begin Tick 
2013-05-14 16:27:23.8894|TRACE|End Tick 
2013-05-14 16:27:28.8969|TRACE|Begin Tick 
2013-05-14 16:27:28.8969|TRACE|End Tick 
2013-05-14 16:27:33.9044|TRACE|Begin Tick 
2013-05-14 16:27:33.9044|TRACE|End Tick 

In the event that an Exception is caught in the OnTick() method, my expectation is that it will be logged, and the service will continue running, such that the OnTick() method will be invoked again in 5 seconds. However, this is not the case, as the callback is never invoked again after the Exception is handled. Why is this?

2013-05-14 16:29:03.8574|TRACE|Begin Start 
2013-05-14 16:29:03.8574|TRACE|End Start 
2013-05-14 16:29:08.8709|TRACE|Begin Tick 
2013-05-14 16:29:09.9750|ERROR|Error in Tick.  System.Net.Sockets.SocketException (0x80004005): No connection could be made because the target machine actively refused it 127.0.0.1:22
2013-05-14 16:29:09.9750|TRACE|End Tick
Michael Hornfeck
  • 1,242
  • 1
  • 16
  • 33

2 Answers2

2
  _timer.Change(5000, Timeout.Infinite);

You didn't create a period timer, passing Infinite for the period argument. The timer will call OnTick() only once. Code is however very incomplete, the only way it could work is when it calls Change() again to recharge the timer.

So high odds that this Change() call is getting bypassed by the exception. If you want to keep it going then this Change() call belongs in the finally block. Although it is not actually a good idea to do this, non-zero odds that the process state is corrupted and it will just crash again. Over and over. You should at least take a countermeasure against this and also count the number of consecutive crashes, giving up when it just bombs repeatedly.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Correct on all points. I edited my original post to show the missing detail. Not sure how I missed that, but it makes perfect sense obviously. – Michael Hornfeck May 15 '13 at 18:13
1

The accepted answer on this StackOverflow post states that in the event of an exception...

System.Threading.Timer will terminate the program.

Which I believe is what you are seeing.

Community
  • 1
  • 1
Belogix
  • 8,129
  • 1
  • 27
  • 32
  • I saw this but thought it was only referring to unhandled exceptions, as mentioned [here](http://stackoverflow.com/a/1718610/213159), but haven't been able to find clarification. – Michael Hornfeck May 15 '13 at 14:00
  • So there is no way we can handle them other than taking whole called function inside try catch block ? – Furkan Gözükara Aug 21 '14 at 12:52