none
Service with a Timer - Debug cursor jumping between different parts of my code

    Question

  • I am writing a service that uses a timer.  I have set the timer to run a method every second in my Service start method.  Inside that method, I stop the timer, run another method, then start the timer again.  So something like this:

    myTimer.stop();
    runMyMethod();
    myTimer.start();

    I tried debugging my code using the debugger.  But what I noticed is that when I F11 into 'runMyMethod' and then stepped through it using F10.  The cursor would occasionally jump to random places.  So inside runMyMethod() there would be lines:

    Line1;
    Line2;
    Line3;
    Line4;
    Line5;

    I would be going through Line1, F10, Line2, F10, then the cursor would jump back to the original method on the line myTimer.stop();.  Then the debugger would start going through my program from that point and then after a while, would 'jump' to Line3 again.  It doesn't always happen at Line 3.  Sometimes at Line 4 or 5, etc and then comes back eventually somewhere down the line.  This is obviously bad but I'm not sure what to do about it.  I guess my question is how do I prevent this from happening?  Is it only happening while I debug?  Or does it happen during normal running of my service?  
    Sunday, May 31, 2009 4:18 PM

Answers

  • LW00,
    no, you should NOT use an infinite loop in your OnStart, OnStop and the like. These methods are supposed to respond to the commands issued via the Service Control Manager (for instance, OnStart fires when you attempt to start the service). If they do not return in a timely fashion, the SCM detects this as an indication that the service is not responding and pulls the plug. The same applies to the other events.

    A common pattern is to have OnStart start a new thread instead and return. If you don't care about the exact timing, and your pauses are there just to make sure that you don't hog too much CPU, you can use a very simple approach:

    Thread workerThread;
    bool stop; // an indicator that you where required to stop the service.

    private void OnStart (...) {
      stop = false;
      workerThread = new Thread (Worker);
      workerThread.Start ();
    }

    private void OnStop (...) {
      stop = true;
      // we wait until the worker thread terminates, so that we give a correct indication to the SCM.
      if (!workerThread.Join (4000)) { // if the thread does not terminate in 4 seconds (choose your own timeout)
        workerThread.Abort (); // then it's stuck somewhere; we resort to killing it instead.
      }
    }

    private void Worker () {
      while (! stop) { // check a variable, so that there is a "soft" way to terminate the thread in response to the OnStop method
        doStuff ();
        Thread.Sleep (1000); // sleep for 1 second
      }
    }

    As you see, both OnStart and OnStop will execute pretty fast (OnStop may have to wait up to the timeout in the worst case). Also, there is obviously no possibility of event overlapping.

    This is just an alternative design to show you how to handle the SCM commands.
    In your case, you could just stop the timer in the OnStop event instead.


    HTH
    --mc

    • Marked as answer by LW00 Tuesday, June 02, 2009 12:06 PM
    Monday, June 01, 2009 3:14 PM

All replies

  • Hi,
    The issue is probably induced by the delay you are creating when debugging (1 second is quite short), but the problem may potentially show up also in production depending on how long it takes for your event code to execute, even considering high loads. Let me explain a little better the situation...

    When a Timer expires (this is not true for System.Windows.Forms.Timer, but I guess you aren't using one of those), it posts its callback to the ThreadPool. Timer.Stop() will prevent more events from being enqueued, but it will not remove any job from the ThreadPool queue. This means that an event may be executed in a different thread even after you call Timer.Stop().

    There is an article mentioning this in the documentation of System.Timers.Timer, which already contains a workaround in the documentation of the Stop() method.

    Your code does not protect you from this event: Start() and Stop() do not count the number of times they are invoked, so if your event gets executed concurrently on two different threads, you may get this unexpected behavior:

    Thread1: Timer.Stop(); // no more events will be queued, but events that were already queued will execute anyway
    Thread1: ...do something...
    Thread1: ...do something...
    Thread2: Timer.Stop(); // the event was already in the ThreadPool queue, so it will execute anyway.
    Thread1: Timer.Start(); // even if we stopped the timer twice, it just takes a single start to enable the timer.
    Thread2: ...do something... // *** the timer is enabled once again, even though Thread2 still thinks that the timer is stopped. ***

    If you really want to avoid this kind of situation you shoud use locking or a simple Mutex to make sure that only one event at a time is executed. If you don't care for more than one event being executed at the same time or even just overlapping, you can simply detect the collision and exit.

    HTH
    --mc


     
    Monday, June 01, 2009 1:45 AM
  • Hi Mario,

    Let me explain what I am trying to do a little more clearly.  At first, I wanted to created a Windows Service which ran an infinite while loop looking at different variables and items and then ran actions according to what these parameters were.  But then I noticed that my Windows Service would not start.  It was because when Windows starts a service, it waits 30 seconds for the OnStart method to exit or else Windows will complain that it could not start the service and exit.  So I said OK.  Instead, I will have an OnStart method that looks like:

    protected override void OnStart(string[] args)
    {
          timer.Start();
    }

    where the method being called looks like:

    void OnElapsed(Object sender, ElapsedEventArgs e)
    {
          timer.Stop();
          doStuff();
          timer.Start();
    }

    This allowed the service's OnStart method to run within 30 seconds while at the same time continuing my polling actions.  Unfortunately, the above scenario with two threads going at the same time happens.  I guess my first question is - Can I use a while loop in my OnStart method that looks something like:

    protected override void OnStart(string[] args)
    {
          while (true)
          {
                doStuff();
                yield();
          }
    }

    So that it is still a while loop but I yield to other programs to allow my OnStart method to exit within 30 seconds?  I'm not sure if this will actually work as the OnStart method didn't actually finish?

    Secondly, I read the documentation for the System.Timers.Timer and the workaround for the Stop() method.  But I'm not sure if I can use it.  Or more likely I am misunderstanding something.  With my current set up with the Service, I don't see how I can use the Control thread to shut off the timer.  I mean, where would I call/start the control thread?  I can not have an infinite loop in my OnStart method so I'm not quite sure where I would call the control thread that controls timer.Stop.  

    So I was thinking.  Could I just do sometihng simple like this:

    protected override void OnStart(string[] args)
    {
          timer.Start();
    }

    void OnElapsed(Object sender, ElapsedEventArgs e)
    {
          int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0);
          if (sync == 0) 
          {
                doStuff();
                syncPoint = 0;
           }
    }

    When the timer method gets called, if it's able to get the lock, grab it.  When another timer thread comes, it won't be able to get the lock until the first one releases it and will just skip excecution of doStuff() all together.  I don't need all the instances of the method to run so this should be sufficient.  What do you think?  
    Monday, June 01, 2009 7:10 AM
  • Having any continually-running service is usually not a good idea. On laptops, for example, any type of infinite loop + yield will still result in 100% CPU and the machine getting quite hot.

    What is it you're monitoring? There are ways to get notifications when files/registry keys are changed.

           -Steve
    Programming blog: http://nitoprograms.blogspot.com/
    I will be in Chicago for the WPF training: http://blogs.msdn.com/jaimer/archive/2009/04/01/announcing-the-using-wpf-to-build-lob-applications-training-tour.aspx
    Monday, June 01, 2009 1:06 PM
  • LW00,
    no, you should NOT use an infinite loop in your OnStart, OnStop and the like. These methods are supposed to respond to the commands issued via the Service Control Manager (for instance, OnStart fires when you attempt to start the service). If they do not return in a timely fashion, the SCM detects this as an indication that the service is not responding and pulls the plug. The same applies to the other events.

    A common pattern is to have OnStart start a new thread instead and return. If you don't care about the exact timing, and your pauses are there just to make sure that you don't hog too much CPU, you can use a very simple approach:

    Thread workerThread;
    bool stop; // an indicator that you where required to stop the service.

    private void OnStart (...) {
      stop = false;
      workerThread = new Thread (Worker);
      workerThread.Start ();
    }

    private void OnStop (...) {
      stop = true;
      // we wait until the worker thread terminates, so that we give a correct indication to the SCM.
      if (!workerThread.Join (4000)) { // if the thread does not terminate in 4 seconds (choose your own timeout)
        workerThread.Abort (); // then it's stuck somewhere; we resort to killing it instead.
      }
    }

    private void Worker () {
      while (! stop) { // check a variable, so that there is a "soft" way to terminate the thread in response to the OnStop method
        doStuff ();
        Thread.Sleep (1000); // sleep for 1 second
      }
    }

    As you see, both OnStart and OnStop will execute pretty fast (OnStop may have to wait up to the timeout in the worst case). Also, there is obviously no possibility of event overlapping.

    This is just an alternative design to show you how to handle the SCM commands.
    In your case, you could just stop the timer in the OnStop event instead.


    HTH
    --mc

    • Marked as answer by LW00 Tuesday, June 02, 2009 12:06 PM
    Monday, June 01, 2009 3:14 PM
  • Thanks Mario and Stephen,

    I am just monitoring databases and network connection between 2 servers.  I will give the threading template you gave a me a try tomorrow at work.  I live in Hong Kong so it's 1 AM.  Thanks for all the help.

    The threading you suggested worked like a charm Mario.  Thanks for all your help.
    Monday, June 01, 2009 5:03 PM