1

I have a Regex which might lead to dead loop when dealing some files. Although I should fix this problem sooner or later, to make it safe, I want to run it in a cancel-able tasks.

I've read some answers here, but none of the following solutions worked. Any help?

    public static Task<CodeFile> CreateCodeFile(string fileName)
    {
        var fileInfo = new FileInfo(fileName);
        foreach (var codeFileFactory in CodeFileFactories)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            cancellationTokenSource.CancelAfter(1000);
            var task = new Task<CodeFile>(() => TryToCreateCodeFile(codeFileFactory, fileInfo),
                cancellationTokenSource.Token);
            task.RunSynchronously(); //Get lost here.
            if (!task.Wait(1000))
                cancellationTokenSource.Cancel();
            
            File.WriteAllTextAsync("FailedLog.txt",
                File.ReadAllTextAsync("FailedLog.txt") + "\r\n" + fileName);
            cancellationTokenSource.Dispose();
            return task;
        }

        return null;
    }

or:

    public static async Task<CodeFile> CreateCodeFile(string fileName)
    {
        var fileInfo = new FileInfo(fileName);
        foreach (var codeFileFactory in CodeFileFactories)
        {
            var cancellationTokenSource = new CancellationTokenSource();
    
            try
            {
                cancellationTokenSource.CancelAfter(1000);
                var codeFile = await Task.Run(() => TryToCreateCodeFile(codeFileFactory, fileInfo), 
                    cancellationTokenSource.Token); //Get lost here.
                if (codeFile != null)
                    return codeFile;
            }
            catch (TaskCanceledException)
            {
                File.WriteAllTextAsync("FailedLog.txt",
                    File.ReadAllTextAsync("FailedLog.txt") + "\r\n" + fileName);
            }
            finally
            {
                cancellationTokenSource.Dispose();
            }
        }
    
        return null;
    }

And here is the function to be called:

    private static CodeFile TryToCreateCodeFile(ICodeFileFactory codeFileFactory, FileInfo fileInfo)
    {
        var codeFile = codeFileFactory.CreateByName(fileInfo);
        return codeFile;
        //Somewhere deeper, .net Regex.Matches() caused the dead loop which I cannot control.
    }

In this topic CancellationTokenSource.CancelAfter not working, I read something like "Your Sleep method is ignoring the CancellationToken." Does it happen in my case?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
cheny
  • 2,545
  • 1
  • 24
  • 30
  • You keep using that cancellation token there... [A Tour of Task, Part 9: Delegate Tasks](https://blog.stephencleary.com/2015/03/a-tour-of-task-part-9-delegate-tasks.html) – Theodor Zoulias Nov 09 '21 at 13:37
  • So far the regex is the only problem, so I would like to user Regex.Timeout first. Maybe do some more research later. Thanks :) cancellationTokenSource.CancelAfter(1000) seems a magical thing, and yet false. – cheny Nov 10 '21 at 03:18

1 Answers1

2

Cancellation tokens implement cooperative cancellation, which means the code being "cancelled" must cooperate, by actively checking the status of the token. Regex.Matches does not have overload which accepts the token, which means it can't be cancelled this way.

Tasks in general can't be magically cancelled. Code which task executes must do that explictly (with something like token.ThrowIfCancellationRequested()), your code doesn't do that so token doesn't do anything useful. Passing token to Task.Run is documented like this:

A cancellation token that can be used to cancel the work if it has not yet started. Run(Action, CancellationToken) does not pass cancellationToken to action.

So yes, if task is not started that will cancel it, not quite useful in your case.

If you perform some long running operation which does not accept and handle cancellation tokens (and you are not able to handle cancellation yourself, by checking token every now and then) - then you can't cancel it with them.

In your case I'd suggest Regex.MatchTimeout

Evk
  • 98,527
  • 8
  • 141
  • 191