Code not executed after cancelation a task

49 views Asked by At

We bind an execution of an asynchronous command to the ui that leads to a Task. Another command cancels this task but it doesn't work properly.

public class MainWindowViewModel : BaseViewModel
{
    private IAsyncCommand _runCancelableCommand;
    private ICommand _cancelCancelableCommand;

    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

    public IAsyncCommand RunCancelableCommand => _runCancelableCommand ??= new AsyncCommand(OnRunCancelableCommand);

    public ICommand CancelCancelableCommand =>
           _cancelCancelableCommand ??= new DelegateCommand(OnCancelCancelableCommand);

    private void OnCancelCancelableCommand()
    {
        try
        {
            cancellationTokenSource.Cancel();
        }
        catch (Exception e)
        {

        }

        // this code here is never reached
    }

    private async Task OnRunCancelableCommand()
    {

        await loadingsomedata(..., cancellationTokenSource.Token);
    }
}

This actually works, the task is canceled correctly. But the code after calling Cancel is never executed. Can anyone say why.

1

There are 1 answers

0
BionicCode On

You have not shown how you actually cancel the operation. Therefore, we can't tell whether your application deadlocks or is stuck in an infinite loop etc.

However, from the way you handle the exception it appears that you have misunderstood the flow. The exception is OperationCancelledException thrown by the code that actually executes the cancellation i.e. polls the CancellationToken and not by the code that calls CancellationTokenSource.Cancel.
Additionally, a once cancelled CancellationTokenSource cannot be reused. It must be disposed and a new instance must be created by the time the cancellable operation is started.

The following example shows how you cancel an async operation properly:

public class MainWindowViewModel : BaseViewModel
{
  public ICommand CancelCommand { get; }
    = new DelegateCommand(ExecuteCancelCommand, CanExecuteCanceLCommand);

  private CancellationTokenSource CancellationTokenSource { get; set; }

  private bool CanExecuteCancelCommand() 
    => this.CancellationTokenSource is not null;

  private void ExecuteCancelCommand()
  {
    this.CancellationTokenSource?.Cancel();

    // This code is now reachable
  }

  private async Task ExecuteCancelableCommand()
  {
    // Once cancelled, a CancellationTokenSource cannot be reused
    this.CancellationTokenSource = new CancellationTokenSource();
    cancellationToken = this.CancellationTokenSource.Token;

    cancellationToken.ThrowIfCancellationRequested();

    try
    {
      await LoadingSomeDataAsync(..., cancellationToken);
    }
    catch (OperationCancelledException)
    {
      // TODO::Handle cancellation e.g., rollback
    }
    finally
    {
      this.CancellationTokenSource.Dispose();
      this.CancellationTokenSource = null;
    }
  }

  private async Task LoadingSomeDataAsync(CancellationToke cancellationToken)
  {
    await Task.Run(() =>
      {
        while (true)
        { 
          cancellationToken.ThrowIfCancellationRequested();
        }
      }, cancellationToken);
  }
}