Writing a Windows Service with a Cancellation Feature

Summary

I’ve blogged about how to write a windows service before (click here). In my previous blog post I did not deep-dive into how to handle a cancel request issued by someone who is clicking on the stop button. In this article, I’m going to expand on the previous article and show how to setup a cancellation token and how to pass this object to another object that can use the cancellation token to determine if it should quit and perform a clean-up.

Using Temp Files

If your windows service uses temp files it’s best to design your code to handle the creation and deletion of temp files in one object using the IDisposable pattern.  For a detailed discussion on how to do an IDisposable pattern you can click here (Implementing IDisposable and the Dispose Pattern Properly).

If you download the sample code, you can find an object named “CreateTempFile”. This object is an example of an object that creates a temp file. In this example, that’s all the object does, and you can use it in that fashion, or you can combine the temp file operation with your write stream operation and make sure you dispose of both objects in the Dispose method. Here’s the CreateTempFile object:

public class CreateTempFile : IDisposable
{
    public string TempFile = "";

    public CreateTempFile()
    {
        TempFile = Path.GetTempFileName();
    }

    public void Dispose()
    {
        Dispose(true);
    }

    ~CreateTempFile()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (TempFile != "")
            {
                File.Delete(TempFile);
            }
        }
    }
}

Now you can use this object inside a “using” statement that will guarantee that the temp file will be cleaned up if the program is exited. Here’s an example of the usage.

using (var dataObject = new CreateTempFile())
{
    // code that uses the temp file here...
} 

Why am I going on and on about using IDisposable? One of the problems with writing code is that you have to have a reasonable expectation that your code will be modified some time in the future. In addition to that fact, your code will, more than likely, be modified by some other software developer. As the code inside the using statement in the toy example above begins to grow, any future developer might not realize that there was a temp file created and needs to be disposed of. The developer working on your code might be adding logic to something that requires a “return” statement and they might not delete the temp file. By using the IDisposable pattern you are guaranteeing that future developers don’t need to worry about deleting the temp file. No matter what method they use to exit from that using statement, a dispose command will be issued and the temp file will be deleted.

Cancelling a Thread

OK, now it’s time to talk about the use of a cancellation token. When you execute your code inside a thread, there is the outer thread that is still running and this is the code that will issue a cancel request. Once a cancel request is issued, it is your thread that is responsible for detecting a cancellation request and performing an orderly exit. Imagine it like a building full of people working 9 to 5. Normally, they will enter the building at 9am and they work all day (I’ll assume they all eat in the cafeteria), and then they go home at 5pm. Now the fire alarm goes off. When the fire alarm goes off, everybody drops what they are doing and walks to the nearest exit and leaves the building. The fire alarm is the cancellation token being passed to the thread (workers).

Let’s pretend we don’t know how to handle a cancellation token. Let’s assume we’re using the code from my previous Windows Service example and we implemented the token using that method. The method I used in the earlier example works for the example and it will work for any program as long as the thread does not call another object that performs a long operation. Here’s some sample code:

public class StartingClass
{
    private volatile bool _stopAgent;
    private static StartingClass _instance;

    public static StartingClass Instance
    {
        get 
        { 
            return _instance ?? (_instance = new StartingClass()); 
        }
    }

    private StartingClass()
    {
        _stopAgent = false;
    }

    public bool Stopped { get; private set; }

    public void Start()
    {
        ThreadPool.QueueUserWorkItem(action =>
        {
            while (!_stopAgent)
            {
                // do something here...

                if (!_stopAgent)
                {
                    Thread.Sleep(5 * 1000); // wait 5 seconds
                }
            }

            Stopped = true;

        }, TaskCreationOptions.LongRunning);
    }

    public void Stop()
    {
        _stopAgent = true;
    }
}

In the sample code above, there is a _stopAgent variable that is initialized to false when the object is created. The main thread creates this object, then it will initiate the thread by calling the “Start()” method. If the main thread wants to stop the inner thread, then it will use the “Stop()” method, which sets the _stopAgent variable to true and is detected by the long running inner thread (in the while statement). The while statement will drop out and set the “Stopped” property to true. The “Stopped” property’s sole purpose is to be read by the master thread to determine if the inner thread has stopped what it is doing. Then the master thread can exit the program. Here’s the master thread of the service object:

public partial class Service1 : ServiceBase
{
    public Service1()
    {
        InitializeComponent();
    }

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

    protected override void OnStop()
    {
        StartingClass.Instance.Stop();

        while (!StartingClass.Instance.Stopped)
        {
            // wait for service to stop
        }
    }
}

I added the wait for stop code to the “OnStop()” method.  This will allow the inner thread time to complete its operation and exit clean.

There is a weakness with this logic and that weakness reveals itself if you create an object inside your thread and call a long running method inside that object. The object cannot access the _stopAgent flag and you can’t just pass the flag to the object (because it will only contain the initial false value when it was passed). You could pass a reference to this value and then it will change when a cancel request is made, but only within the method it is passed to. There is an easier way…

Using a Cancellation Token

If you download the sample code that goes along with this blog post (see link at bottom), you’ll notice that I have refactored the windows service to use an object called CancellationTokenSource. The new StartingClass object looks like this:

public class StartingClass
{
    private static StartingClass _instance;
    public CancellationTokenSource cts = new  
           CancellationTokenSource();

    public static StartingClass Instance
    {
        get 
        { 
            return _instance ?? (_instance = new StartingClass()); 
        }
    }

    public bool Stopped { get; private set; }

    public void Start()
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(action =>
        {
            CancellationToken token = (CancellationToken)action;
            while (!token.IsCancellationRequested)
            {
                var dataProcessingObject = new 
                        DataProcessingObject(action);
                dataProcessingObject.PerformProcess();

                if (!token.IsCancellationRequested)
                {
                    Thread.Sleep(5 * 1000); // wait 5 seconds
                }
            }

            Stopped = true;

        }), cts.Token);    }

    public void Stop()
    {
        cts.Cancel();
    }
}

As you can see in the above code, I have replaced the boolean value of_stopAgent with the new “cts” variable. The “action” object is passed in to the DataProcessingObject as a constructor. The DataProcessingObject class looks like this:

public class DataProcessingObject
{
    private CancellationToken token;

    public DataProcessingObject(object action)
    {
        token = (CancellationToken)action;
    }

    public void PerformProcess()
    {
        // do something here
        using (var dataObject = new CreateTempFile())
        {
            using (StreamWriter writer = new StreamWriter(dataObject.TempFile, true))
            {
                for (int i = 0; i < 5000; i++)
                {
                    writer.WriteLine("Some test data");

                    // check for cancel signal
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }

                    // this is not necessary (only for this demonstration 
                    // to force this process to take a long time).
                    Thread.Sleep(1000);
                }
            }
        }
    }
}

A new CancellationToken is created inside the DataProcessingObject. This will receive the cancellation request from the calling method from the action object. Inside the DataProcessingObject, the “IsCancellationRequested” boolean flag must be watched to determine if a long running process must end and exit the object. This will provide a clean exit (due to the fact that the CreateTempFile() object will be exited before the program shuts down and delete any temp files created.

Download the Code

You can go to my github account and download the entire working example by clicking here.

Leave a Reply