Wrapping an APM implementation with Future<T>

Wrapping an APM implementation with Future<T>

  • Comments 8

In a previous post, I talked about implementing the Asynchronous Programming Model pattern using Future<T> from Parallel Extensions to the .NET Framework.  It's also possible to go in the opposite direction, to create a Future<T> from an existing APM implementation.

As has been shown in previous examples, in this example we'll take advantage of the "promise" capabilities provided by Future<T>.  This allows a Future<T> to be created without a Func<T> delegate, such that the future's Value and Exception properties can be set explicitly, but only once; any thread waiting on the Future<T> will block until one of these properties is set.

static Future<T> Create<T>(
    Action<AsyncCallback> beginFunc,
    Func<IAsyncResult, T> endFunc)
{
    var f = Future<T>.Create();
    beginFunc(iar => {
        try { f.Value = endFunc(iar); }
        catch (Exception e) { f.Exception = e; }
    });
    return f;
}

This Create<T> method first creates a Future<T> as a promise.  It then calls a delegate provided by the caller to start the asynchronous action using the relevant BeginXx method; as an argument to that action, this code passes a delegate that will be called back by the asynchronous operation when it completes; the APM method will pass an IAsyncResult, which will then be passed in this callback to the endFunc delegate provided by the caller.  That endFunc will call the EndXx method, and will use the resulting value or exception to set the Future<T>.

As an example of using this, consider a FileStream.  FileStream exposes a BeginRead method:

IAsyncResult BeginRead(
    byte[] array, int offset, int numBytes,
    AsyncCallback
userCallback, object stateObject)

as well as a corresponding EndRead method:

int EndRead(IAsyncResult asyncResult);

If we want to create a Future<T> that represents an asynchronous read operation on a FileStream, we could do so with our new Create method as follows:

var readFuture = Create<int>(
   
ac => fs.BeginRead(buffer, 0, buffer.Length, ac, null),
   
fs.EndRead);

I can now go off and do something else, and when I later get readFuture's Value, I'll block until the asynchronous operation completes (or return immediately with the result if it's already completed).  If the FileStream was created to support asynchronous I/O operations and if the underlying version of Windows supports asynchronous I/O, while the read request is happening, no threads will be used, and yet I'll still have a valid Future<T> to represent the operation.

There are of course other possible designs for and implementations of such a Create method.  An alternate implementation might look like this:

static Future<T> Create<T>(
    IAsyncResult iar, Func<IAsyncResult, T> endFunc)
{
    var f = Future<T>.Create();
    ThreadPool.RegisterWaitForSingleObject(
            iar.AsyncWaitHandle, delegate
{
        try { f.Value = endFunc(iar); }
        catch (Exception e) { f.Exception = e; }
    }, null, -1, true);
    return f;
}

Here's I'm using the same promise functionality of Future<T>, but instead of using the callback functionality of the APM, I'm relying on ThreadPool's RegisterWaitForSingleObject functionality.  When the provided IAsyncResult's AsyncWaitHandle is signaled (which will happen when the asynchronous operation completes), my delegate will be called to call endFunc and set the Future<T>'s Value or Exception property as shown previously.  This version can be used as follows:

var readFuture = Create<int>(
    fs.BeginRead(buffer, 0, buffer.Length, null, null),
    fs.EndRead);

Now comes the interesting question...

We don't currently have a Create method like this on Future<T>.  You can write your own as has been shown here, but is this important enough for us to include one on Future<T> itself?  Do you have such a need in your applications?  Should we provide deeper support to further enable this kind of integration? Let us know :)

Leave a Comment
  • Please add 7 and 4 and type the answer here:
  • Post
  • Yes please!

    I was trying to do EXACTLY that with a web service client last month.

    This would make the code a lot cleaner when dealing with multiple WS calls, which are intrinsecally parallel.

  • Yes, please include. Like Diego said, it would make APM-using code so much cleaner, and that implies easier to write, read and maintain, and probably also less bugs (if you do your job right ;-) )

    One thing though: your second version seems easier to use (as it does not require an async callback) but will use a thread from the threadpool, while the first one doesn't.

    That's a tradeoff I'm not sure you can make, so you'd have to provide both overloads. Would you agree? If not, I'd say go for the first one, with the callback.

    Kris.

  • I have just got finished doing something similar for ascyc calls of WCF Channels.  That would be great to have.

  • PingBack from http://dvanderboom.wordpress.com/2008/07/03/concurrency-with-futures/

  • This sounds nice, but you completely lose the whole point of the APM, which is to not block a thread.  When ever you call the getter of the Future<T>.Value property, you will end up blocking a thread.

  • Seanberg, thanks for the comment, but I'm not understanding the concern.  If you need the value right here and now, then you access Value, and yes, the accessing thread may block, but if you need the value immediately for some work happening on the accessing thread, what other options do you have (besides, of course, restructuring your code so you don't need it here and now)  Note that using Value isn't the only way to access the Future's value.  You can, for example, use ContinueWith to be called back when the future completes, and you'll be passed the completed future so that you can access its value, exception, etc.  That won't block any threads.

    Having an APM implementation represented as a Future<T> (which in upcoming bits has been renamed to Task<TResult>) enables composition with other asynchronous operations, which means you can do things like running some other task when N APM implementations have completed.

    As requested by comments on this thread, we have implemented similar (but more robust) APM support in TPL. Stay tuned... I think you'll be pleased.

  • Just a quick comment -

    shouldn't there be a try-catch around the begin func as well? - so that any exception thrown by the begin func can be handled in the same place, i.e. at the time you resolve the future's result.

  • Hi Phil-

    It's a good question, and a trade-off.  In the APM pattern, if the BeginXx method throws an exception, the asynchronous operation hasn't been kicked off.  Thus, at that point, it's just like an exception escaping from a synchronous call to Xx, and is typically due to invalid arguments or invalid context or the like.  While having one place to handle all exceptions (e.g. in the resulting future) might appear easier, it also conflates what typically amounts to two different reasons for exceptions, and they're usually handled differently, i.e. you'll do one thing if the asynchronous operation couldn't be started at all, and you'll do another thing if the asynchronous operation could be started but failed.

Page 1 of 1 (8 items)