Leveraging TaskCompletionSource

The TaskCompletionSource class is the means by which we can create and “manage” a Task ourselves.  By “manage” I mean implement the underlying mechanism that embodies the progression of an asynchronous activity including its completion or possible termination due to exceptions.

This is particularly useful in situations where we may be able to complete the task’s work synchronously – for example we might be able to cache data that we’re asynchronously retrieving from a network so that the first time we retrieve it we do indeed wait on network IO but subsequent requests for the same data can be met by simply retrieving a cached copy.

The fragment below is a method which returns a Task<T> that represents the asynchronous execution of a DNS name resolution operation. The DNS methods conform to the APM pattern where the BeginXXX method accepts a callback that is invoked by the system when the operation eventually completes and that callback (should) calls the associated EndXXX method to formally complete the operation.


private static Task<IPAddress[]> resolve(string domain)
{

    TaskCompletionSource<IPAddress[]> completer;

    completer = new TaskCompletionSource<IPAddress[]>();

    Dns.BeginGetHostAddresses(domain,

    (async_status) =>
    {
        TaskCompletionSource<IPAddress[]> completion;

        completion = (TaskCompletionSource<IPAddress[]>)(async_status.AsyncState);

        try
        {
            IPAddress[] result = Dns.EndGetHostAddresses(async_status) ;
            completion.SetResult(result);
        }
        catch (Exception exc)
        {
            completion.SetException(exc);
        }

    }, completer);

    return completer.Task;
}

By adding a simple dictionary based cache we are able to improve this implementation of Task and complete the activity rapidly and synchronously – on the very same thread that calls the resolve method.

private static Task<IPAddress[]> resolve(string domain)
{

    TaskCompletionSource<IPAddress[]> completer;

    completer = new TaskCompletionSource<IPAddress[]>();

    if (cache.ContainsKey(domain))
       {
       completer.SetResult(cache[domain]);
       return (completer.Task);
       }

    Dns.BeginGetHostAddresses(domain,

    (async_status) =>
    {
        TaskCompletionSource<IPAddress[]> completion;

        completion = (TaskCompletionSource<IPAddress[]>)(async_status.AsyncState);

        try
        {
            IPAddress[] result = Dns.EndGetHostAddresses(async_status) ;
            cache.TryAdd(domain,result);
            completion.SetResult(result);
        }
        catch (Exception exc)
        {
            completion.SetException(exc);
        }

    }, completer);

    return completer.Task;
}

Because this method returns a Task<T> it is straightforward to use await to invoke the operation too.

But do we need TaskCompletionSource?

With the advent of C# 5 and the support for async and await, code like the above can be written much more simply and without any reliance upon TaskCompletionSource:


private async static Task<IPAddress[]> resolve(string domain)
{
    if (cache.ContainsKey(domain))
       return(cache[domain]);

    IPAddress[] result = await Dns.GetHostAddressesAsync(domain);

    cache.TryAdd(domain,result);

    return (result);
}

See how simple that code is? Its very simple but it does of course rely upon Dns.GetHostAddressesAsync which of course creates and returns the Task<T> for us.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s