Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tasks are getting cancelled, but it's not clear why and how

I'm working on a large monolith web application running on ASP.NET framework and added a feature. I've build some abstraction in order to run several tasks in parallel.

public interface IImporter
{
    Action[] GetProcessActions();
}

public class ImportTaskFactory : IDisposable
{
    private readonly Task[] _tasks;

    /// <summary>
    /// Immediately starts all the Process tasks
    /// </summary>
    public ImportTaskFactory(CancellationToken cancellationToken, Action<Task> onTaskFaultedAction, IEnumerable<IImporter> importers)
    {
        _tasks = importers
            .SelectMany(importer =>
                importer
                    .GetProcessActions()
                    .Select(action =>
                        Task.Factory.StartNew(action, cancellationToken)
                                    .ContinueWith(onTaskFaultedAction, TaskContinuationOptions.OnlyOnFaulted)))
            .ToArray();
    }

    public void WaitAll(CancellationToken cancellationToken)
    {
        Task.WaitAll(_tasks, cancellationToken);
    }

    public void Dispose()
    {
        foreach (var task in _tasks)
        {
            task?.Dispose();
        }
    }
}

and it's called like

//...
var importers = new IImporter[]
{
   //...three classes deriving from IImporter
}
using (var importTasks =
    new ImportTaskFactory(_cancellationTokenSource.Token, OnTaskFaulted, importers))
    {
        importTasks.WaitAll(_cancellationTokenSource.Token);
    }
//... where:
    private void OnTaskFaulted(Task task)
    {
        Log.Logline(task.Exception.ToString());
        _cancellationTokenSource.Cancel();
    }
//...

However, when running the tasks start out fine and do their work, but seem to stop for unclear reason, each throwing an System.Threading.Tasks.TaskCanceledException (no inner exception). If I look at the passed CancellationToken, cancellation was not requested.

enter image description here

I've been trying to debug this for a couple of hours, but have no idea what's happening. How can the tasks get cancelled this way? How can I debug this? I mean how can I see what triggers the cancellation?

I've set breakpoints in the OnTaskFaulted method, but it never seems to be called... That's the only point where _cancellationTokenSource.Cancel(); is called.

like image 797
JHBonarius Avatar asked Sep 12 '25 09:09

JHBonarius


1 Answers

This not an answer to your question. It just shows how to do what you want, without getting these pesky cancellation exceptions:

void ImportTaskFactory(CancellationToken cancellationToken,
    Action<Task> onTaskFaultedAction, IEnumerable<IImporter> importers)
{
    _tasks = importers
        .SelectMany(importer => importer
            .GetProcessActions()
            .Select(action => Task.Run(() =>
            {
                try
                {
                    action();
                }
                catch (Exception ex)
                {
                    onTaskFaultedAction(Task.FromException(ex));
                }
            }, cancellationToken))).ToArray();
}

I replaced the Task.Factory.StartNew with Task.Run, because the former requires to pass explicitly the scheduler every time you use it.

Probably it would be simpler if the onTaskFaultedAction parameter was of type Action<Exception> instead of Action<Task>.

like image 145
Theodor Zoulias Avatar answered Sep 14 '25 23:09

Theodor Zoulias