-1

I have a class that represents a custom Task. This class has an Execution method that returns the IEnumerable<string> of all the inside yield return statement, and a Start method that calls the enumerable, thus executing the lazy code.

My problem is that I have also a Stop method that basically cancels the token associated with the Start method by using:

TokenSource.CancelAfter(delay);
TokenSource.Token.ThrowIfCancellationRequested();

This code actually stops the running task, but how can I handle the exception? This is not done inside the actual Task.

Any ideas on how to handle the stop?


An example I've been using to test this part:

public class FunctionTask : Awaitable
{
    public FunctionTask(string code, Scheduler scheduler = null) 
    : base(code, scheduler)
    { }

    public override IEnumerable<string> Execution()
    {
         for (int i = 0; i < 10; i++)
         {
             yield return WaitFor(TimeSpan.FromSeconds(10d))
                 .WithDescription($"Step {i + 1}, waiting 1 second");
         }

        yield return "Done";
    }
}

And these are the (inherited) start and stop methods:

public Task Start()
{
    stopRequested = false;
    Status.Value = TaskStatus.WaitingToRun;

    Task task = new Task(() =>
        {
            Status.Value = TaskStatus.Running;

            try
            {
                // Iterate through the Execution task state
                IEnumerable<string> executionState = Execution();
                foreach (string state in executionState)
                    WaitState.Value = state;

                // And then through the termination task state
                IEnumerable terminationState = Termination();
                foreach (string state in terminationState)
                    WaitState.Value = state;

                Status.Value = TaskStatus.RanToCompletion;
            }
            catch (Exception ex) // This may be caused by a stop request or an actual exception
            {
                Status.Value = TaskStatus.Faulted;

                if (stopRequested)
                    Logger.Error(ex);
                else
                    Logger.Warn($"Task with code {Code} faulted because a stop has been requested");
            }
        },
        TokenSource.Token
    );

    task.Start(Scheduler);
    return task;
}

public virtual void Stop()
{
    if (Status.Value == TaskStatus.Running || Status.Value == TaskStatus.WaitingToRun)
    {
        stopRequested = true;

        TokenSource.Cancel();
        TokenSource.Token.ThrowIfCancellationRequested(); // Let the task fail and then stop

        Status.Value = TaskStatus.Canceled;
    }
}

Bloom Dee
  • 37
  • 7
  • 1
    Your `Execution()` method needs access to the `CancellationToken` (either as method parameter or as private field of the instance) and must regulary check its state. If the state is set, the method has to return or to throw, depending on your needs. But there is nothing you can do _from outside the method_. – Oliver Mar 16 '23 at 11:23
  • 1
    Or alternative, it can use the `Register` method to add a callback that does something active that can stop cancellation, for example disposing a socket or marking a `Channel` as completed (or whatever you need to actually stop things). As a side note: for async, maybe `IAsyncEnumerable` is worth looking at (over `IEnunerable`) – Marc Gravell Mar 16 '23 at 11:24
  • 3
    Can you please add a full [mre]? – Guru Stron Mar 16 '23 at 12:12
  • Why are you calling `TokenSource.Token.ThrowIfCancellationRequested();` immediately after the `TokenSource.Cancel();`? – Theodor Zoulias Mar 16 '23 at 13:32

1 Answers1

3

Use the property IsCancellationRequested if you don't want to throw the OperationCanceledException.

In the Stop method just call TokenSource.Cancel() and remove the TokenSource.Token.ThrowIfCancellationRequested(), it doesn't make sense there.

Then use the IsCancellationRequested property to stop the enumeration, something like:

foreach (string state in executionState)
{
    if(token.IsCancelltionRequested)
        break;

    WaitState.Value = state;
}
Alberto
  • 15,626
  • 9
  • 43
  • 56