Abhijeet Patel
Abhijeet Patel

Reputation: 6878

Providing synchronous and asynchronous versions of an API in library code

Is there a recommended best practices/guidance for exposing synchronous and asynchronous versions of an API in a class library? For instance, if I have the following 2 methods defined in a class library:

  public Task<string> GetSomeDataAsync()
    {
        //typically an IO operation that would be awaited on, simplified to return a Task for illustration
        return Task<string>.Factory.StartNew( () =>  "foo");
    }

    public string GetSomeDataSync()
    {
        var task = GetSomeDataAsync();
        task.ConfigureAwait(continueOnCapturedContext: false);
        return task.Result;
    }

The above could be hosted in a Winform/WPF/Console/ASP.NET client app. Is it safe for a client to use the synchronous version above which uses task.Result, given that the task is configured not to capture any synchronization context to avoid potential deadlocks

Upvotes: 4

Views: 1158

Answers (2)

svick
svick

Reputation: 244998

I think the best advice on this comes from two articles by Stephen Toub:

The second one is more relevant for you. In short, it says that you shouldn't do this. If the caller of your method decides to wait synchronously, he still can make that choice and he is in a better position than you to do it (because he knows the environment, so he should know if deadlocks are an issue, for example).

The above could be hosted in a Winform/WPF/Console/ASP.NET client app. Is it safe for a client to use the synchronous version above which uses task.Result, given that the task is configured not to capture any synchronization context to avoid potential deadlocks

You're wrong about that. ConfigureAwait() doesn't modify the Task in any way (especially since the Task is already executing at that point). All it does is to return a ConfiguredTaskAwaitable and if you await that, it won't resume on the captured context.

This means your method would still deadlock.

Upvotes: 5

Haney
Haney

Reputation: 34912

There are 3 "major" options for async patterns which are endorsed by MS. I'd recommend that you pick one of the patterns and adhere to it... It looks like you're already headed towards the "Task-based Asynchronous Pattern" so you'd have very few mods in order to conform to the pattern. This allows you to create your methods in a way that will be uniform and intuitive to other consumers... They'll know the pattern from the .NET libraries that MS has built. See this for details:

Overview: http://msdn.microsoft.com/en-us/library/jj152938.aspx

Task Async Pattern: http://msdn.microsoft.com/en-us/library/hh873175.aspx

An example of your methods using the Task Asynchronous Pattern with support for cancellation tokens:

public Task<string> GetSomeDataAsync(CancellationToken cancellationToken = null)
{
    return Task<string>.Factory.StartNew(() =>  "foo", cancellationToken);
}

// NOTE: no need to append Sync to the method name, redundant.
public string GetSomeData()
{
    var task = GetSomeDataAsync();
    task.ConfigureAwait(continueOnCapturedContext: false);
    return task.Result;
}

Upvotes: -1

Related Questions