Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TPL cancellation continuation never called on cancelled Task

I have the following setup in my code that utilizes the TPL:

  • One field in my class: private CancellationTokenSource _cancellationTokenSource;
  • This CancellationTokeSource gets instatiated every time I create a TPL task that uses that particular cancellationtoken

The actualy TPL tasks look like this:

var dataRetrievalTask = new Task<List<myType>>(() =>
            {
                // retrieve data and do something
                foreach (var a in retrievalMethod())
                {
                    if (_cancellationTokenSource.Token.IsCancellationRequested)
                        _cancellationTokenSource.Token.ThrowIfCancellationRequested();

                        // do something if not cancelled
                    }
                }

                return filledListOfMyType;

            }, _cancellationTokenSource.Token);

            // define what shall happen if data retrievel finished without any problems
            var writingLoadedDataToGridTask = dataRetrievalTask.ContinueWith(task =>
            {
              // do something in case we ran to completion without error
            }, _cancellationTokenSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, currentScheduler);

            // what to do in case cancellation was performed
            var loadingDataCancelledTask = dataRetrievalTask.ContinueWith(task =>
                              {
                                someLabel.Text = "Data retrieval canceled.";
                              },_cancellationTokenSource.Token, TaskContinuationOptions.OnlyOnCanceled, currentScheduler);

            // what to do in case an exception / error occured
            var loadingDataFaulted = dataRetrievalTask.ContinueWith(task =>
                {
                    someLabel.Text = string.Format("Data retrieval ended with an Error.");
                }, _cancellationTokenSource.Token, TaskContinuationOptions.OnlyOnFaulted, currentScheduler);

            // when any of the continuation tasks ran through, reset ui controls / buttons etc
            Task.Factory.ContinueWhenAny(new[] { writingLoadedDataToGridTask, loadingDataCancelledTask, loadingDataFaulted }, task =>
            {
              // reset controls and all that
            }, _cancellationTokenSource.Token, TaskContinuationOptions.None, currentScheduler);


            dataRetrievalTask.Start();

Now my problem is that when the _cancellationTokenSource.Cancel() method is called somewhere (in a Cancel-button's .Click event handler) that particular loadingDataCancelledTask's body/method isn't called.

What am I doing wrong here? I am using and handing over the same _cancellationTokenSource.Token instance... and everything else (the 'writingLoadedDataToGridTask' and 'loadingDataFaulted' tasks & the following 'Task.Factory.ContinueWhenAny(new[] { writingLoadedDataToGridTask, loadingDataCancelledTask, loadingDataFaulted }, task => ...' block) do actually work. Only cancellation does not. Does anyone see/know why?

like image 830
Jörg Battermann Avatar asked May 12 '12 11:05

Jörg Battermann


People also ask

Which object do you inspect to determine if a long running task will be Cancelled?

The background long-running tasks are cancelled by using the CancellationToken object in Blazor.

What is task Cancelled exception?

TaskCanceledException(String, Exception, CancellationToken) Initializes a new instance of the TaskCanceledException class with a specified error message, a reference to the inner exception that is the cause of this exception, and the CancellationToken that triggered the cancellation. TaskCanceledException(Task)

What is the property of the task token to check the request of task cancellation?

If the tokens are same and the token's IsCancellationRequested property returns true , the task interprets this as acknowledging cancellation and transitions to the Canceled state.


1 Answers

Your cancellation continuation is being cancelled because it uses the same cancellation token.

If you think about it it makes total sense: When you say "I want to cancel all processing" you actually get what you ask for. All processing stops, including updating the UI.

The solution is to not use the cancellation token for the cancel, error and ContinueWhenAny continuations. That way these continuations always run.

like image 157
usr Avatar answered Oct 30 '22 11:10

usr