Donnie Sparko
Donnie Sparko

Reputation: 59

Timeout for async task

My application currently executes Adobe Illustrator with some command. Waits when result file appears in some exact folder (with async function) and does something with file when it's ready.

But the problem is, sometimes Adobe Illustrator fails and app keeps waiting. In such cases I can't figure out, how can I apply timeout mechanism to kill Adobe Illustrator and skip current process.

Here is the code:

...
await WhenFileCreated(result_file_name);
if (File.Exists(result_file_name))
{
...


public static Task WhenFileCreated(string path)
{
    if (File.Exists(path))
        return Task.FromResult(true);

    var tcs = new TaskCompletionSource<bool>();
    FileSystemWatcher watcher = new FileSystemWatcher(Path.GetDirectoryName(path));

    FileSystemEventHandler createdHandler = null;
    RenamedEventHandler renamedHandler = null;
    createdHandler = (s, e) =>
    {
        if (e.Name == Path.GetFileName(path))
        {
            tcs.TrySetResult(true);
            watcher.Created -= createdHandler;
            watcher.Dispose();
        }
    };

    renamedHandler = (s, e) =>
    {
        if (e.Name == Path.GetFileName(path))
        {
            tcs.TrySetResult(true);
            watcher.Renamed -= renamedHandler;
            watcher.Dispose();
        }
    };

    watcher.Created += createdHandler;
    watcher.Renamed += renamedHandler;

    watcher.EnableRaisingEvents = true;

    return tcs.Task;
}

How to apply timeout to this? Any suggestions?

Upvotes: 5

Views: 15886

Answers (2)

spender
spender

Reputation: 120450

The simplest way would be to race a Task.Delay against the actual task:

await Task.WhenAny(WhenFileCreated(result_file_name), 
                   Task.Delay(TimeSpan.FromSeconds(5));

A better way would be implement cancellation in your async method

public static Task WhenFileCreated(string path, 
                                   CancellationToken ct = 
                                       default(CancellationToken))
{
     //...
     var tcs = new TaskCompletionSource<bool>();
     ct.Register(() => tcs.TrySetCanceled())
     //...
}

...and then pass in a cancellation token with a timeout:

using(var cts = new CancellationTokenSource(5000))
{
    try
    {
        await WhenFileCreated(string path, cts.Token);
    }
    catch(TaskCanceledException)
    {
        //...
    }
}

Upvotes: 10

RBreuer
RBreuer

Reputation: 1391

Try modifying your code to set this timeout for example.

var tcs = new TaskCompletionSource<TestResult>();

const int timeoutMs = 20000;
var ct = new CancellationTokenSource(timeoutMs);
ct.Token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: false);

More details you can find in: Timeout an async method implemented with TaskCompletionSource

Upvotes: 0

Related Questions