Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping Native Async functions with Task<T>

I'm attempting to write a c# wrapper around a third-party library written in native code for consumption in our apps, which are almost exclusively written in .NET, and I'm trying to remain faithful to the C# patterns. Almost all the calls in this library are asynchronous in nature, and it would seem appropriate to wrap all my async calls into Task<T> objects. Here's an oversimplified example of how the native library is structured:

delegate void MyCallback(string outputData);

class MyNativeLibrary
{
    public int RegisterCallback(MyCallback callback); // returns -1 on error
    public int RequestData(string inputData);         // returns -1 on error
}

Right now, I've provided my return values through event subscription, however I believe this would be a far better way to return my data:

class WrapperAroundNativeCode
{
    public async Task<string> RequestData(string inputData);
}

So far I've been unsuccessful in finding an appropriate way to implement this, and I'm reaching out to folks with more experience in working with Task<T> objects and the async/await pattern than I do.

like image 713
Joey Herrington Avatar asked Oct 21 '13 13:10

Joey Herrington


People also ask

Can we use async without task?

Async code can be used for both I/O-bound and CPU-bound code, but differently for each scenario. Async code uses Task<T> and Task , which are constructs used to model work being done in the background. The async keyword turns a method into an async method, which allows you to use the await keyword in its body.

Can async Task Return Value C#?

Async methods can have the following return types: Task, for an async method that performs an operation but returns no value. Task<TResult>, for an async method that returns a value. void , for an event handler.

What is the difference between task run and async await?

An await expression in an async method doesn't block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method. The async and await keywords don't cause additional threads to be created.

How does async task work in C#?

An async method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete. In the meantime, control returns to the caller of the method, as the example in the next section shows.


2 Answers

You would use a TaskCompletionSource<TResult> for this. Something along the lines of the following code:

class WrapperAroundNativeCode
{
    public async Task<string> RequestData(string inputData)
    {
        var completionSource = new TaskCompletionSource<string>();
        var result = Native.RegisterCallback(s => completionSource.SetResult(s));
        if(result == -1)
        {
            completionSource.SetException(new SomeException("Failed to set callback"));
            return completionSource.Task;
        }

        result = Native.RequestData(inputData);
        if(result == -1)
            completionSource.SetException(new SomeException("Failed to request data"));
        return completionSource.Task;
    }
}

This answer assumes that there won't be concurrent calls to this method. If there were you would need some way to differentiate between the different calls. Many APIs provide a userData payload that you can set to a unique value per call, so that you can differentiate.

like image 166
Daniel Hilgarth Avatar answered Oct 08 '22 21:10

Daniel Hilgarth


It sounds like you're looking for TaskCompletionSource<T>. You'd wrap your library by creating a TaskCompletionSource, creating an instance of MyNativeLibrary and registering a callback which set the result of the task completion source, and then requesting data from same instance. If either of these steps fails, set an error on the task completion source. Then just return the value of the TaskCompletionSource<>.Task property to the caller.

(This is assuming you can create separate instances of MyNativeLibrary - if you can only create a single instance across your whole app, it gets a lot harder.)

like image 35
Jon Skeet Avatar answered Oct 08 '22 22:10

Jon Skeet