Skip to content

Stop or Pause Jobs

Hartono Halim edited this page Aug 23, 2017 · 1 revision

Pause

The pause token is an implementation of the PauseTokenSource described by Stephen Taub athttps://blogs.msdn.microsoft.com/pfxteam/2013/01/13/cooperatively-pausing-async-methods/. Since Shift does not support the async-await pattern, the pauseToken.WaitWhilePausedAsync() must be executed synchronously, such as:

pauseToken.WaitWhilePausedAsync().GetAwaiter().GetResult();

Similar to the CancellationToken, the pause action can only be executed when the WaitWhilePausedAsync() is hit within a loop. Detailed explanation in What's wrong with Tasks CancellationToken? section.

Stop

Shift server runs jobs using the System.Threading.Tasks managed thread pool Tasks.

The System.Threading.Tasks allows the .NET framework to manage the Tasks life-cycle automatically, it also allows the server to notify the client when the task is being canceled. To allow client jobs to be notified, use CancellationToken place holder as another optional parameter in each of your jobs. More details is in MSDN article How to Cancel a Task and Its Children

The cancellation token is a placeholder to notify Shift server that the job is able to handle cancellation requests when notified. Shift server will instantiate its own cancellation token and pass it to the job before invocation, read the JobServer.CreateTask() method for more info.

An example of adding cancellation token to the job parameter:

private static void AddJob()
{
    var job = new TestJob();
    var progress = new SynchronousProgress<ProgressInfo>();
    var token = (new CancellationTokenSource()).Token; 
    var jobID = jobClient.Add("Shift.Demo.Client", () => job.Start("Hello World", progress, token));
}

public class TestJob
{
    public void Start(string value, IProgress<ProgressInfo> progress, CancellationToken token)
    {
        var total = 10;

        var note = "";
        for (var i = 0; i < total; i++)
        {
            if (token.IsCancellationRequested)
            {
                token.ThrowIfCancellationRequested(); //throw OperationCanceledException
            }

            //Report progress here
            //Do long running work here
        }

        return;
    }
}

The CancellationToken usage is only a request and it is up to the job itself to implement job cancellation. It is not required to pass the CancellationToken, hence some jobs are not cancelable and can only be stopped by turning off the server process.

What's wrong with Tasks CancellationToken?

Because in long running process, there needs to be a sure way to cancel/stop the process without killing the entire server application and kill other unrelated jobs. The CancellationToken is just a request, not a guarantee. A running task keeps running until the underlying job complete, throws an exception, or throws a OperationCanceledException from a cancel request.

There is no clean way to use CancellationToken without a loop in .NET System.Threading.Tasks

One way to cancel Task is to throw the token.ThrowIfCancellationRequested(); after each line, discussed in http://stackoverflow.com/a/24312446/2437862. However this does not fulfill the requirement to cancel operation in the middle of the long running process.

using System.Threading.Tasks;
Task.Factory.StartNew(()=> 
{
    // Do long running process 1
    token.ThrowIfCancellationRequested();

    // Do long running process 2
    token.ThrowIfCancellationRequested();
}, token);

With the above example, modified from the stackoverflow thread, the only way to stop the running task while in the middle of long running process 1 or 2 is to kill the main application, and will kill all other running tasks. The nature of long running jobs/tasks is that they're taking a substantial amount of time. What's the point of canceling a job or task after the work is finished? So the use of CancellationToken is workable only when you have multiple smaller jobs that can be interrupted by cancellation event, it does not work for long running jobs.

Cancellation in Managed Threads

Here is an MSDN article about Cancellation in Managed Threads

The object cancellation discussed in the MSDN article section Operation Cancellation Versus Object Cancellation was also considered for the Shift server job cancellation. However this method of cancellation requires that the each jobs implement a method that cancel itself.

class CancelableObject
{
    public string id;

    public CancelableObject(string id)
    {
        this.id = id;
    }

    public void Cancel() 
    { 
        Console.WriteLine("Object {0} Cancel callback", id);
        // Perform object cancellation here.
    }

    public void Start() 
    {
        // Long running process here
    }
}

The CancelableObject.Cancel() method must do the clean up of the long running job and also stop the job. How do you stop the long running job in the main CancelableObject.Start() method? Probably have to use the object own cancellation tokens.